diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index bf857f4..0000000 Binary files a/.DS_Store and /dev/null differ diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml new file mode 100644 index 0000000..ebc90af --- /dev/null +++ b/.github/workflows/linux.yml @@ -0,0 +1,93 @@ +# Linux — the primary gate, and the cheap one. Full Python matrix, browser tests +# included, plus the documented-numbers check. +# +# The package is pure Python (one py3-none-any wheel), so nothing is compiled here; +# what the three OS workflows actually prove is that the *runtime* behaves the same +# everywhere — path handling, SQLite file locking, threading, and the browser render. +name: Linux + +on: + push: + branches: [main] + pull_request: + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + # A superseded PR run is wasted runner time. A push to main is not cancelled: + # when CI goes red we want to know which commit did it. + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +jobs: + test: + # Named explicitly so the check context is unambiguous. Three workflows with a + # job called "test" produce three checks called "test", and branch protection + # cannot then require a specific one. + name: Linux ${{ matrix.python-version }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.10", "3.11", "3.12", "3.13"] + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + cache: pip + cache-dependency-path: pyproject.toml + + - name: Install + run: | + python -m pip install --upgrade pip + pip install -e ".[dev]" + + # Browsers are ~100 MB and never change between runs; downloading them on + # every job is the single most wasteful thing this workflow could do. + # The key includes the Playwright version, so a bump invalidates it. + - name: Playwright version + id: pw + run: echo "version=$(python -c 'import playwright; print(playwright.__version__)')" >> "$GITHUB_OUTPUT" + + - name: Cache Playwright browsers + id: pw-cache + uses: actions/cache@v4 + with: + path: ~/.cache/ms-playwright + key: pw-${{ runner.os }}-${{ steps.pw.outputs.version }} + + - name: Install browser + # --only-shell is the headless shell alone: about a third of the download, + # and the tests never open a headed browser. + run: python -m playwright install chromium --only-shell --with-deps + if: steps.pw-cache.outputs.cache-hit != 'true' + + - name: Install browser dependencies + # System libraries are not in the cached browser directory. + run: python -m playwright install-deps chromium + if: steps.pw-cache.outputs.cache-hit == 'true' + + - name: Lint + run: python -m ruff check . + + - name: Test + run: python -m pytest tests/ -q -m "not ollama" + + # One gate: lint, tests and the documented numbers pass together. Keeping this + # as a separate optional target is how a README drifts from what the code does. + - name: Check documented numbers + run: python scripts/check_numbers.py + + - name: Build the wheel + run: | + pip install build + python -m build + + - name: Wheel installs and runs clean + run: | + python -m venv /tmp/fresh + /tmp/fresh/bin/pip install --quiet dist/*.whl + /tmp/fresh/bin/mpe-lkg --version + /tmp/fresh/bin/python -c "import mpe_lkg; a = mpe_lkg.create_app(); assert a.test_client().get('/').status_code == 200" diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml new file mode 100644 index 0000000..4f41978 --- /dev/null +++ b/.github/workflows/macos.yml @@ -0,0 +1,60 @@ +# macOS — one Python version, full suite including the browser render tests. +# +# A single version on purpose: macOS runners cost ten times a Linux minute, and the +# Python-version axis is already covered on Linux. What this run is actually for is +# the platform axis — arm64, a different SQLite build, a different browser build. +name: macOS + +on: + push: + branches: [main] + pull_request: + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +jobs: + test: + # Explicit, so branch protection can require this exact context. + name: macOS + runs-on: macos-latest + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + cache: pip + cache-dependency-path: pyproject.toml + + - name: Install + run: | + python -m pip install --upgrade pip + pip install -e ".[dev]" + + - name: Playwright version + id: pw + run: echo "version=$(python -c 'import playwright; print(playwright.__version__)')" >> "$GITHUB_OUTPUT" + + - name: Cache Playwright browsers + id: pw-cache + uses: actions/cache@v4 + with: + path: ~/Library/Caches/ms-playwright + key: pw-${{ runner.os }}-${{ steps.pw.outputs.version }} + + - name: Install browser + run: python -m playwright install chromium --only-shell + if: steps.pw-cache.outputs.cache-hit != 'true' + + - name: Lint + run: python -m ruff check . + + - name: Test + run: python -m pytest tests/ -q -m "not ollama" + + - name: Check documented numbers + run: python scripts/check_numbers.py diff --git a/.github/workflows/pypi.yml b/.github/workflows/pypi.yml new file mode 100644 index 0000000..4ef60f5 --- /dev/null +++ b/.github/workflows/pypi.yml @@ -0,0 +1,83 @@ +# Publish to PyPI on a version tag. +# +# The package is pure Python, so there is exactly one artefact to build -- a +# py3-none-any wheel that serves Linux, macOS and Windows on every supported +# interpreter. No cibuildwheel matrix, no cross-compilation, nothing to sign per +# platform. +# +# Publishing is gated on the three OS workflows being green for the same commit; +# a tag is not a reason to skip the tests. +name: PyPI + +on: + push: + tags: ["v*"] + workflow_dispatch: + inputs: + dry_run: + description: "Build and check only, do not upload" + type: boolean + default: true + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + cache: pip + cache-dependency-path: pyproject.toml + + - name: Install + run: | + python -m pip install --upgrade pip + pip install -e ".[dev]" build twine + + - name: Lint + run: python -m ruff check . + + # Browser tests are skipped here: the OS workflows already ran them for this + # commit. What must not be skipped is that the code imports and behaves. + - name: Test + run: python -m pytest tests/ -q -m "not ollama" --ignore=tests/test_render.py + + - name: Build + run: python -m build + + - name: Check metadata + run: python -m twine check dist/* + + - name: The tag must match the version in pyproject + if: startsWith(github.ref, 'refs/tags/v') + run: | + TAG="${GITHUB_REF_NAME#v}" + PKG=$(python -c "import tomllib,pathlib;print(tomllib.loads(pathlib.Path('pyproject.toml').read_text())['project']['version'])") + echo "tag=$TAG pyproject=$PKG" + test "$TAG" = "$PKG" || { echo "::error::tag $TAG does not match version $PKG"; exit 1; } + + - uses: actions/upload-artifact@v4 + with: + name: dist + path: dist/ + + publish: + needs: build + runs-on: ubuntu-latest + if: startsWith(github.ref, 'refs/tags/v') || inputs.dry_run == false + environment: pypi + permissions: + id-token: write # for trusted publishing, once it is configured + steps: + - uses: actions/download-artifact@v4 + with: + name: dist + path: dist/ + + - uses: pypa/gh-action-pypi-publish@release/v1 + with: + # Remove this once Trusted Publishing is set up for the project on PyPI, + # which removes the need to hold a token at all. + password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml new file mode 100644 index 0000000..66e2349 --- /dev/null +++ b/.github/workflows/windows.yml @@ -0,0 +1,65 @@ +# Windows — the platform most likely to find a real bug in this codebase. +# +# Not because of the language, but because of the file semantics: an open SQLite +# handle cannot be deleted on Windows, path separators differ, and the temporary +# directories pytest hands out are cleaned up differently. Those are exactly the +# places a store-backed web app breaks. +name: Windows + +on: + push: + branches: [main] + pull_request: + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +jobs: + test: + # Explicit, so branch protection can require this exact context. + name: Windows + runs-on: windows-latest + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + cache: pip + cache-dependency-path: pyproject.toml + + - name: Install + run: | + python -m pip install --upgrade pip + pip install -e ".[dev]" + + - name: Playwright version + id: pw + shell: bash + run: echo "version=$(python -c 'import playwright; print(playwright.__version__)')" >> "$GITHUB_OUTPUT" + + - name: Cache Playwright browsers + id: pw-cache + uses: actions/cache@v4 + with: + path: ~\AppData\Local\ms-playwright + key: pw-${{ runner.os }}-${{ steps.pw.outputs.version }} + + - name: Install browser + run: python -m playwright install chromium --only-shell + if: steps.pw-cache.outputs.cache-hit != 'true' + + - name: Lint + run: python -m ruff check . + + - name: Test + run: python -m pytest tests/ -q -m "not ollama" + + - name: Check documented numbers + run: python scripts/check_numbers.py + + - name: Console script works + run: mpe-lkg --version diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..64fb6f8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,20 @@ +__pycache__/ +*.py[cod] +.venv/ +venv/ +.pytest_cache/ +.ruff_cache/ + +# Runtime artefacts written into the working directory +embeddings.db +embeddings.ann + +# docs/claims/*.json is deliberately NOT ignored: those files are the record of +# what was measured, and scripts/check_numbers.py verifies the README against them. + +.DS_Store + +# Build artefacts +dist/ +build/ +*.egg-info/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..39a9f2e --- /dev/null +++ b/Makefile @@ -0,0 +1,46 @@ +PY := .venv/bin/python + +.PHONY: all venv lint test test-ollama measure sweep bench check-numbers run clean + +# One gate. Lint, tests and the documented numbers pass together or the build is +# not green -- keeping the numbers in a separate optional target is how a README +# drifts away from what the code actually does. +all: lint test check-numbers + +# pyproject.toml is the single source of truth for dependencies; there is no +# requirements.txt to drift out of sync with it. +venv: + uv venv --python 3.12 .venv + uv pip install --python $(PY) -e ".[dev]" + $(PY) -m playwright install chromium --only-shell + +lint: + $(PY) -m ruff check . + +test: + $(PY) -m pytest tests/ -q -m "not ollama" + +# Requires a running Ollama with the models named in the README. +test-ollama: + $(PY) -m pytest tests/ -q -m ollama + +# Regenerates docs/claims/*.json from a live Ollama. +measure: + $(PY) scripts/measure.py + +# How well does each layer of a local model separate topics? Needs torch. +sweep: + $(PY) scripts/layer_sweep.py + +# Exact scan versus an approximate index, at several store sizes. +bench: + $(PY) scripts/bench_search.py + +check-numbers: + $(PY) scripts/check_numbers.py + +run: + $(PY) app.py + +clean: + rm -rf .pytest_cache .ruff_cache __pycache__ tests/__pycache__ embeddings.db embeddings.ann diff --git a/README.md b/README.md index 1818862..d6ca699 100644 --- a/README.md +++ b/README.md @@ -1,55 +1,242 @@ # Local Knowledge Graph -![Example](example.png) +[![Linux](https://github.com/punnerud/Local_Knowledge_Graph/actions/workflows/linux.yml/badge.svg?branch=main)](https://github.com/punnerud/Local_Knowledge_Graph/actions/workflows/linux.yml) +[![macOS](https://github.com/punnerud/Local_Knowledge_Graph/actions/workflows/macos.yml/badge.svg?branch=main)](https://github.com/punnerud/Local_Knowledge_Graph/actions/workflows/macos.yml) +[![Windows](https://github.com/punnerud/Local_Knowledge_Graph/actions/workflows/windows.yml/badge.svg?branch=main)](https://github.com/punnerud/Local_Knowledge_Graph/actions/workflows/windows.yml) +[![PyPI](https://img.shields.io/pypi/v/mpe-lkg.svg)](https://pypi.org/project/mpe-lkg/) +[![Python](https://img.shields.io/pypi/pyversions/mpe-lkg.svg)](https://pypi.org/project/mpe-lkg/) -This application uses a local Llama model to answer queries, build embeddings, and create a knowledge graph for exploring related questions and answers. +![Example](docs/example.png) -## Description +Ask a local Llama model a question, watch it reason step by step, and see the steps drawn as a +knowledge graph where the edges are the semantic similarity between them. -The Local Knowledge Graph is a Flask-based web application that leverages a local Llama language model to process user queries, generate step-by-step reasoning, and visualize the thought process as an interactive knowledge graph. It also finds and displays related questions and answers based on semantic similarity. +Everything runs on your machine. Nothing is uploaded anywhere. -## Features +## Install -- Interactive web interface for submitting queries -- Step-by-step reasoning process displayed in real-time -- Dynamic knowledge graph visualization of the reasoning steps -- Calculation and display of the strongest reasoning path -- Related questions and answers based on semantic similarity -- Local processing using a Llama language model +```bash +pip install mpe-lkg +mpe-lkg +``` -## Usage +Then open . -1. Ensure you have all the required dependencies installed. -2. Start the Flask application by running `app.py`. -3. Open a web browser and navigate to `http://localhost:5100` (or the appropriate port if modified). -4. Enter your query in the input field and click "Submit". -5. Watch as the application generates a step-by-step reasoning process, updating the knowledge graph in real-time. -6. Review the final answer and the strongest reasoning path. -7. Explore related questions and answers displayed below the main response. +`mpe-lkg doctor` reports whether Ollama is reachable, which models are installed, and the exact +`ollama pull` command for anything missing. It exits non-zero when something is wrong, so it +works in a script. -## Requirements +
+Install from source instead -- Python 3.7+ -- Flask -- NumPy -- scikit-learn -- Annoy -- NetworkX -- A local Llama language model (e.g., llama3.1:8b) running on `http://localhost:11434` +```bash +git clone https://github.com/punnerud/Local_Knowledge_Graph +cd Local_Knowledge_Graph +python3 -m venv .venv +.venv/bin/pip install -e . +.venv/bin/mpe-lkg +``` -## Installation +`python app.py` still works from a clone as it always has — it is a shim over the same code. -1. Clone this repository. -2. Install the required Python packages using the requirements.txt file: - ``` - pip install -r requirements.txt - ``` -3. Ensure you have a local Llama model running and accessible. -4. Run the Flask application: - ``` - python app.py - ``` +
-## Note +
+Use it from Python -This application requires a local Llama language model to be running and accessible. Make sure you have the appropriate model set up and running before using this application. \ No newline at end of file +```python +from mpe_lkg import create_app, health + +print(health()) # {'ok': True, 'models': [...], ...} +create_app().run(port=5100) +``` + +
+ +### You also need Ollama + +[Ollama](https://ollama.com) running locally, with one chat model and, ideally, one embedding +model: + +```bash +ollama pull llama3.2:3b # or llama3.1:8b, or any chat model you already have +ollama pull nomic-embed-text # optional but recommended, see "Which embedding model" below +``` + +Python 3.10 or newer. The wheel is `py3-none-any`, so there is nothing to compile and the same +artefact serves Linux, macOS and Windows — all three are tested on every push. + +## Configuration + +Everything is an environment variable, and the defaults work unchanged. + +| Variable | Default | Meaning | +|---|---|---| +| `OLLAMA_URL` | `http://localhost:11434` | Where Ollama is listening | +| `LKG_CHAT_MODEL` | `llama3.1:8b` | The model that does the reasoning | +| `LKG_EMBED_MODEL` | *(auto)* | Embedding model. Empty means: use an installed embedding model if there is one, otherwise fall back to the chat model | +| `LKG_HOST` / `LKG_PORT` | `127.0.0.1` / `5100` | Where the app listens | +| `LKG_DEBUG` | off | Set to `1` for the Flask debugger. Do not do this on a shared network | + +## Which embedding model, and why it matters + +The edges in the graph are cosine similarities, so how much they vary decides whether the +picture tells you anything. Measured over four unrelated six-step reasoning chains +(`make measure`, recorded in `docs/claims/edge_spread.json`): + +| Model | Dimensions | Mean edge weight | Coefficient of variation | +|---|---|---|---| +| `all-minilm` | 384 | 0.48 ± 0.07 | **0.28 ± 0.11** | +| `nomic-embed-text` | 768 | 0.67 ± 0.05 | **0.13 ± 0.03** | + +The uncertainties are the spread across the four topics. The larger model produces the +*less* discriminative graph here: under `nomic-embed-text` almost every pair of reasoning +steps scores around 0.67, so the edge labels stop distinguishing anything. This is ordinary +distance concentration, and it is a good reason to look at the spread rather than trusting +that a better retrieval model draws a better graph. `all-minilm` is the better default for +the *drawing* even though it is the weaker retriever. + +Both work. Any embedding size works — nothing in the code assumes a dimension. + +## Embeddings from inside a model + +An embedding endpoint gives you one pooled vector from the top of the stack. You can instead +tap a chosen point *inside* a local model — which also makes models with no embedding API +usable, since a forward pass is all that is required: + +```bash +pip install torch transformers + +LKG_EMBED_BACKEND=hf \ +LKG_HF_MODEL=HuggingFaceTB/SmolLM2-135M \ +LKG_HF_LAYER=blocks.-1 \ +mpe-lkg +``` + +Layers are addressed structurally, not by a per-architecture path: `blocks.0`, `blocks.12`, +`blocks.-1`, `blocks.-1.mlp`, or any explicit dotted module path. The block stack is found by +looking for the longest `nn.ModuleList` whose children share one class, which covers Llama, +Qwen, Mistral, Gemma, Phi, GPT-2, GPT-NeoX, Falcon, BERT, ViT and CLIP without a lookup table. +`LKG_HF_POOLING` selects `last` (default, and the only architecturally correct choice for a +decoder under a causal mask), `mean`, or `cls`. + +### Does the depth matter? + +`make sweep` runs the same four-topic corpus through several layers and reports how far each +one puts steps of the same topic from steps of a different topic. On `SmolLM2-135M`: + +| Layer | Within topic | Across topics | Separation | +|---|---|---|---| +| `blocks.0` | 0.998 | 0.996 | **0.002** | +| `blocks.7` | 0.895 | 0.834 | 0.062 | +| `blocks.15` | 0.914 | 0.863 | 0.051 | +| `blocks.22` | 0.893 | 0.780 | 0.113 | +| `blocks.29` | 0.926 | 0.779 | **0.148** | + +The first block cannot tell the topics apart at all — it sees each token before any context +has been mixed in — and that near-zero is the control that says the separation deeper in is +real rather than an artefact of the metric. Separation grows roughly seventyfold with depth. + +Two details that quietly ruin a layer comparison if you skip them, and which this handles: +intermediate blocks emit the raw residual stream while the model's own last hidden state has +already been through the final norm, so that norm is applied to every layer to put them in one +space; and the states are captured with forward hooks that pool inside the hook rather than +with `output_hidden_states=True`, which would materialise every layer at once — several +gigabytes on an 8B model before any pooling happens. + +## Development + +```bash +make venv # uv-based environment, including a headless browser for the render tests +make all # ruff + pytest + the documented numbers, in one gate +make test # unit, stream and browser tests; no model needed +make test-ollama # the tests that need a live Ollama +make measure # re-measure the embedding-model table into docs/claims/ +make sweep # re-measure the per-layer separation table (needs torch) +make bench # exact scan vs an approximate index, at several store sizes +``` + +`make all` runs `scripts/check_numbers.py`, which resolves every measurable claim in this +README to a value in `docs/claims/`. If a number here stops being true, the build fails +instead of the README quietly becoming wrong. Some of its checks are ground truths computed +from arithmetic rather than from a previous run, because a consistency gate cannot detect a +consistent error. + +## Troubleshooting + +**The page stays blank when I submit.** +Open . It reports whether Ollama answered, which models are +installed, and what to pull. Errors are now shown in the page itself rather than only in the +browser console. + +**It says a model is not found.** +The default chat model is `llama3.1:8b`. If you have a different one, either pull that, or +set `LKG_CHAT_MODEL` to a model you already have. + +**Ollama runs in Docker or on another machine.** +Set `OLLAMA_URL`, and make sure Ollama binds beyond localhost (`OLLAMA_HOST=0.0.0.0`). + +**It seemed to hang and never printed anything.** +That was a real bug: two retry paths could loop forever without ever sending anything to the +browser. Both are bounded now, and the stream sends a heartbeat while the model is thinking. + +## How it works + +| File | Responsibility | +|---|---| +| `src/mpe_lkg/app.py` | Flask routes and server-sent-event framing | +| `src/mpe_lkg/backends.py` | Chat and embedding backends, model discovery, health checks | +| `src/mpe_lkg/reasoning.py` | The step-by-step loop | +| `src/mpe_lkg/graph.py` | Similarity, graph construction, strongest path | +| `src/mpe_lkg/store.py` | SQLite storage and exact nearest-neighbour search | +| `src/mpe_lkg/layers.py` | Embeddings read from inside a model | + +The strongest path maximises the product of the similarities along it, which is the same as +minimising a sum of `-log(similarity)`. Those costs are non-negative, so Dijkstra gives the +exactly optimal path, and the number reported is the geometric mean of the edges on it. + +## Why the similarity search has no approximate index + +The store keeps growing — it is no longer wiped between questions, so "Related Questions and +Answers" can actually surface earlier ones — which makes it fair to ask whether it needs an +ANN index. Measured with `scripts/bench_search.py` at 768 dimensions: + +| Vectors | Exact scan (numpy) | Annoy query | Annoy build, per insert | +|---|---|---|---| +| 100 | 0.007 ms | 0.031 ms | 3.5 ms | +| 1 000 | 0.017 ms | 0.032 ms | 36 ms | +| 10 000 | 0.30 ms | 0.031 ms | 366 ms | +| 100 000 | 3.3 ms | 0.032 ms | 4 020 ms | + +Three things follow. + +**The exact scan is already fast enough at any plausible size.** Hundreds of vectors cost +about 0.02 ms, against an LLM call that takes seconds. Even a hundred thousand costs 3 ms. + +**An Annoy index cannot be appended to.** It is immutable once built, and this app inserts +after every reasoning step, so the whole index has to be rebuilt on each one. That is the +last column, and it is worse than the exact scan at every size measured. + +**On a current numpy the index returns wrong answers.** With `annoy` 1.17.3 and numpy 2.5.2 on +Python 3.12, `get_nns_by_item(7, 5)` returns `[1]` — one result instead of five, and not the +vector itself, which must always be its own nearest neighbour at distance zero. That is +reproducible in a clean environment built from the old `requirements.txt`, which means the +"Related Questions" panel was silently returning a single arbitrary row. + +The last point is pinned as a check that fails if a future build ever starts behaving, so the +decision can be revisited rather than inherited. `tests/test_store.py` asserts exactness +directly: a vector is its own nearest neighbour, and the ranking matches a full brute-force +sort. + +If the store ever does grow past a few hundred thousand vectors, the argument that changes +first is memory, not speed — 100 000 × 768 × 4 bytes is about 300 MB held in RAM — and the +answer then is a memory-mapped index, not a faster query. + +## Licence + +MIT. + +--- + +Published to PyPI as `mpe-lkg` — **M**orten **P**unnerud-**E**ngelstad **L**ocal +**K**nowledge **G**raph. diff --git a/app.py b/app.py index 172aa74..ad660d6 100644 --- a/app.py +++ b/app.py @@ -1,506 +1,17 @@ -from flask import Flask, render_template, request, jsonify, Response, stream_with_context -import requests -import json -import time -import re -import sqlite3 -import numpy as np -from sklearn.metrics.pairwise import cosine_similarity -from annoy import AnnoyIndex -import os -import networkx as nx -import heapq +#!/usr/bin/env python3 +"""Compatibility shim: `python app.py` still works after the move to src/mpe_lkg. -app = Flask(__name__) +The project has been a clone-and-run-app.py app since 2024 and the README said so +for two years, so that has to keep working whether or not the package is installed. +Everything real lives in src/mpe_lkg/. +""" -# Function to get embeddings from the API -def get_embedding(text): - headers = {'Content-Type': 'application/json'} - data = json.dumps({"model": "llama3.1:8b", "input": text}) - response = requests.post('http://localhost:11434/api/embed', headers=headers, data=data) - - if response.status_code != 200: - raise Exception(f"API request failed with status code {response.status_code}: {response.text}") - - response_data = response.json() - - if 'embedding' in response_data: - return np.array(response_data['embedding'], dtype=np.float32) - elif 'embeddings' in response_data and response_data['embeddings']: - return np.array(response_data['embeddings'][0], dtype=np.float32) - else: - raise KeyError(f"No embedding found in API response. Response: {response_data}") +import pathlib +import sys -# Database functions -def create_database(): - conn = sqlite3.connect('embeddings.db') - c = conn.cursor() - c.execute('''CREATE TABLE IF NOT EXISTS embeddings - (id INTEGER PRIMARY KEY, text TEXT, embedding BLOB, is_question INTEGER)''') - conn.commit() - return conn +sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent / "src")) -def insert_data(conn, text, embedding, is_question): - c = conn.cursor() - c.execute("INSERT INTO embeddings (text, embedding, is_question) VALUES (?, ?, ?)", - (text, sqlite3.Binary(np.array(embedding).tobytes()), is_question)) - conn.commit() +from mpe_lkg.app import app, run # noqa: E402,F401 (re-exported for old imports) -# Annoy index functions -def build_annoy_index(conn, vector_size=4096, n_trees=10): - c = conn.cursor() - c.execute("SELECT COUNT(*) FROM embeddings") - total_vectors = c.fetchone()[0] - - annoy_index = AnnoyIndex(vector_size, 'angular') - c.execute("SELECT id, embedding FROM embeddings") - - for i, (id, embedding_blob) in enumerate(c.fetchall()): - embedding = np.frombuffer(embedding_blob, dtype=np.float32) - if len(embedding) != vector_size: - print(f"Warning: Embedding size mismatch. Expected {vector_size}, got {len(embedding)}. Skipping this vector.") - continue - annoy_index.add_item(id - 1, embedding) - - print("Building index...") - annoy_index.build(n_trees) - annoy_index.save('embeddings.ann') - print("Index built and saved") - -def find_similar(conn, query_embedding, top_k=5): - annoy_index = AnnoyIndex(4096, 'angular') - annoy_index.load('embeddings.ann') - - similar_ids, distances = annoy_index.get_nns_by_vector(query_embedding, top_k, include_distances=True) - - c = conn.cursor() - results = [] - for id, distance in zip(similar_ids, distances): - c.execute("SELECT text, is_question FROM embeddings WHERE id = ?", (id + 1,)) - text, is_question = c.fetchone() - similarity = 1 - distance - results.append((id + 1, text, similarity, bool(is_question))) - - return results - -# Llama model interaction functions -def stream_api_call(messages, max_tokens): - prompt = json.dumps(messages) - data = { - "model": "llama3.1:8b", - "prompt": prompt, - "max_tokens": max_tokens, - "temperature": 0.2, - "stream": True - } - try: - response = requests.post('http://localhost:11434/api/generate', - headers={'Content-Type': 'application/json'}, - data=json.dumps(data), - stream=True) - response.raise_for_status() - full_response = "" - for line in response.iter_lines(): - if line: - chunk = json.loads(line.decode('utf-8')) - if 'response' in chunk: - full_response += chunk['response'].replace("'",'') - yield chunk['response'].replace("'",'') - if full_response: - return json.loads(full_response.replace("'",'')) - else: - raise ValueError("Empty response from API") - except Exception as e: - error_message = f"Failed to generate response. Error: {str(e)}" - return {"title": "Error", "content": error_message, "next_action": "final_answer"} - -def extract_json(text): - text = re.sub(r'```(?:json)?\s*', '', text) - text = text.strip() - json_objects = re.findall(r'\{[^{}]*\}', text) - - if json_objects: - try: - return json.loads(json_objects[-1]) - except json.JSONDecodeError: - pass - - return { - "title": "Parsing Error", - "content": text, - "next_action": "continue" - } - -def calculate_similarity(embedding1, embedding2): - return cosine_similarity([embedding1], [embedding2])[0][0] - -def get_short_title(content): - messages = [ - {"role": "system", "content": "You are a concise summarizer. Provide a very short title (under 20 characters) for the given content."}, - {"role": "user", "content": f"Summarize this in under 20 characters: {content[:100]}..."} - ] - - title_data = "" - for chunk in stream_api_call(messages, 50): - title_data += chunk - - short_title = title_data.strip()[:20] - return short_title - -def calculate_strongest_path(graph_data, current_step): - G = nx.Graph() - for node in graph_data['nodes']: - G.add_node(node['id']) - for edge in graph_data['edges']: - G.add_edge(edge['from'], edge['to'], weight=edge['value']) - - start_node = 'Step1' - end_node = f'Step{current_step}' - - def dijkstra(graph, start, end): - queue = [(0, start, [])] - visited = set() - - while queue: - (cost, node, path) = heapq.heappop(queue) - if node not in visited: - visited.add(node) - path = path + [node] - - if node == end: - path_length = len(path) - 1 - if path_length == 0: # Handle the case when there's only one node - return 1.0, path # Return perfect similarity for single node - return -cost / path_length, path # Return average similarity - - for neighbor in graph.neighbors(node): - if neighbor not in visited: - edge_weight = graph[node][neighbor]['weight'] - new_cost = cost - edge_weight # Accumulate total similarity - heapq.heappush(queue, (new_cost, neighbor, path)) - - return None, None - - try: - avg_similarity, path = dijkstra(G, start_node, end_node) - if path: - if len(path) == 1: # Handle the case when there's only one node - return path, [], 1.0 - path_edges = list(zip(path[:-1], path[1:])) - path_weights = [G[u][v]['weight'] for u, v in path_edges] - return path, path_weights, avg_similarity - else: - return None, None, None - except nx.NetworkXNoPath: - return None, None, None - -def generate_response(prompt, conn): - messages = [ - {"role": "system", "content": """You are an expert AI assistant that explains your reasoning step by step. For each step, provide a title that describes what you're doing in that step, along with the content. Decide if you need another step or if you're ready to give the final answer. Respond in JSON format with 'title', 'content', and 'next_action' (either 'continue' or 'final_answer') keys. USE AS MANY REASONING STEPS AS POSSIBLE. AT LEAST 3. BE AWARE OF YOUR LIMITATIONS AS AN LLM AND WHAT YOU CAN AND CANNOT DO. IN YOUR REASONING, INCLUDE EXPLORATION OF ALTERNATIVE ANSWERS. CONSIDER YOU MAY BE WRONG, AND IF YOU ARE WRONG IN YOUR REASONING, WHERE IT WOULD BE. FULLY TEST ALL OTHER POSSIBILITIES. YOU CAN BE WRONG. WHEN YOU SAY YOU ARE RE-EXAMINING, ACTUALLY RE-EXAMINE, AND USE ANOTHER APPROACH TO DO SO. DO NOT JUST SAY YOU ARE RE-EXAMINING. USE AT LEAST 3 METHODS TO DERIVE THE ANSWER. USE BEST PRACTICES."""}, - {"role": "user", "content": prompt}, - {"role": "assistant", "content": "Thank you! I will now think step by step following my instructions, starting at the beginning after decomposing the problem."} - ] - - steps = [] - step_count = 1 - total_thinking_time = 0 - - graph_data = { - 'nodes': [], - 'edges': [] - } - embeddings = [] - edge_dict = {} # New dictionary to keep track of edges - - def serialize_graph_data(graph_data): - serialized = { - 'nodes': graph_data['nodes'], - 'edges': [ - { - 'from': edge['from'], - 'to': edge['to'], - 'value': float(edge['value']), # Convert float32 to regular float - 'label': f"{float(edge['value']):.2f}", # Add similarity value as label - 'font': {'size': 10} # Adjust font size for readability - } - for edge in graph_data['edges'] - ] - } - #print("serialized",serialized) - return serialized - - def calculate_top_similarities(embeddings, current_step, top_k=2): - similarities = [] - for i in range(min(current_step, len(embeddings))): - if i < len(embeddings) and current_step < len(embeddings): - similarity = float(calculate_similarity(embeddings[current_step], embeddings[i])) - similarities.append((i, similarity)) - similarities.sort(key=lambda x: x[1], reverse=True) - return similarities[:top_k] - - max_steps = 20 # Set a maximum number of steps to prevent infinite loops - final_answer = None # Initialize final_answer - - while step_count < max_steps: - start_time = time.time() - step_data = "" - for chunk in stream_api_call(messages, 300): - step_data += chunk - end_time = time.time() - thinking_time = end_time - start_time - - step_json = extract_json(step_data) - title = step_json.get('title', '') - content = step_json.get('content', 'No content') - next_action = step_json.get('next_action', 'continue') - - # Check if content exceeds 700 characters - if len(content) > 700: - print(f"Step {step_count} content exceeded 700 characters. Retrying...") - messages.append({"role": "user", "content": "Your last response was too long. Please provide a more concise version of your last step."}) - continue # Skip the rest of the loop and try again - - # If we reach here, the step is valid and under 700 characters - total_thinking_time += thinking_time - - # Calculate embedding for the current step - embedding = get_embedding(content) - embeddings.append(embedding) - insert_data(conn, content, embedding, False) - - # Generate a short title only if the original title is empty or too long - if not title or len(title) > 20: - short_title = get_short_title(content) - else: - short_title = title[:20] # Truncate the original title if it's longer than 20 characters - - # Generate a unique node ID - node_id = f"Step{step_count}" - while node_id in [node['id'] for node in graph_data['nodes']]: - step_count += 1 - node_id = f"Step{step_count}" - - # Add node for this step - graph_data['nodes'].append({ - 'id': node_id, - 'label': f"Step {step_count}: {short_title}" - }) - - if step_count > 1 and len(embeddings) > 1: - top_similarities = calculate_top_similarities(embeddings, len(embeddings) - 1, top_k=2) - - # Clear previous edges for the current step - edge_dict = {k: v for k, v in edge_dict.items() if v['to'] != node_id} - - for prev_step, similarity in top_similarities: - prev_node_id = f"Step{prev_step + 1}" - if prev_node_id in [node['id'] for node in graph_data['nodes']]: # Only create edges to existing nodes - edge_key = f"{prev_node_id}-{node_id}" - edge_dict[edge_key] = { - 'from': prev_node_id, - 'to': node_id, - 'value': similarity, - 'length': 300 * (1 - similarity) - } - - # Update graph_data['edges'] with the current edge_dict - graph_data['edges'] = list(edge_dict.values()) - # Scale node sizes based on average similarity - connected_similarities = [edge['value'] for edge in edge_dict.values() if edge['from'] == node_id or edge['to'] == node_id] - if connected_similarities: - avg_similarity = sum(connected_similarities) / len(connected_similarities) - graph_data['nodes'][-1]['value'] = avg_similarity * 30 + 10 # Scale to 10-40 range - else: - graph_data['nodes'][-1]['value'] = 20 # Set a default size if no connections - - serialized_graph_data = serialize_graph_data(graph_data) - strongest_path, path_weights, avg_similarity = calculate_strongest_path(serialized_graph_data, step_count) - - path_data = { - 'strongest_path': strongest_path, - 'path_weights': path_weights, - 'avg_similarity': avg_similarity - } if strongest_path is not None else None - - yield f"data: {json.dumps({'type': 'step', 'step': step_count, 'title': title, 'content': content, 'graph': serialized_graph_data, 'path_data': path_data})}\n\n" - - steps.append((f"Step {step_count}: {title}", content, thinking_time)) - messages.append({"role": "assistant", "content": json.dumps(step_json)}) - - if next_action == 'final_answer' and step_count <= 5: - print("Final answer requested but not enough steps provided. Continuing...") - messages.append({ - "role": "user", - "content": f"You've only provided {step_count - 1} steps of 5. Can you look for possible error or alternatives to your answer. Continue your reasoning." - }) - continue - elif next_action == 'final_answer' or 'boxed' in content.lower(): - if not final_answer: - final_answer = content # Set final_answer if not already set - - # Add last evaluation step - messages.append({ - "role": "user", - "content": f"Let's do a final evaluation. The original question was: '{prompt}'. Based on your reasoning, is your final answer correct and complete? If not, what might be missing or incorrect?" - }) - - start_time = time.time() - evaluation_data = "" - for chunk in stream_api_call(messages, 300): - evaluation_data += chunk - end_time = time.time() - thinking_time = end_time - start_time - total_thinking_time += thinking_time - - evaluation_json = extract_json(evaluation_data) - evaluation_content = evaluation_json.get('content', 'No evaluation content') - - # Check if the evaluation suggests a different answer - if check_consistency(final_answer, evaluation_content): - break # Exit the loop if consistent - else: - print("Inconsistency detected. Restarting the reasoning process.") - yield f"data: {json.dumps({'type': 'inconsistency', 'message': 'Inconsistency detected. Restarting the reasoning process.'})}\n\n" - messages = messages[:2] # Reset messages to initial state - step_count += 1 # Increment step count instead of resetting - final_answer = None # Reset final_answer - graph_data = {'nodes': [], 'edges': []} # Reset graph data - embeddings = [] - edge_dict = {} - continue - - step_count += 1 # Increment step count only for valid steps - - # Generate final answer if not already provided - if not final_answer: - messages.append({"role": "user", "content": "Please provide the final answer based on your reasoning above."}) - - start_time = time.time() - final_data = "" - for chunk in stream_api_call(messages, 200): - final_data += chunk - end_time = time.time() - thinking_time = end_time - start_time - total_thinking_time += thinking_time - - final_json = extract_json(final_data) - final_answer = final_json.get('content', final_data) - - # Calculate embedding for the final answer - final_embedding = get_embedding(final_answer) - insert_data(conn, final_answer, final_embedding, False) - - # Add final answer node to the graph - final_node_id = f"Step{step_count}" - while final_node_id in [node['id'] for node in graph_data['nodes']]: - step_count += 1 - final_node_id = f"Step{step_count}" - - graph_data['nodes'].append({ - 'id': final_node_id, - 'label': f"Final Answer: {get_short_title(final_answer)}" - }) - - # Calculate similarities with previous steps for the final answer - top_similarities = calculate_top_similarities(embeddings + [final_embedding], step_count - 1, top_k=2) - - for prev_step, similarity in top_similarities: - prev_node_id = f"Step{prev_step + 1}" - if prev_node_id in [node['id'] for node in graph_data['nodes']]: # Only create edges to existing nodes - edge_key = f"{final_node_id}-{prev_node_id}" - edge_dict[edge_key] = { - 'from': final_node_id, - 'to': prev_node_id, - 'value': similarity, - 'length': 300 * (1 - similarity) - } - - graph_data['edges'] = list(edge_dict.values()) - - serialized_graph_data = serialize_graph_data(graph_data) - strongest_path, path_weights, avg_similarity = calculate_strongest_path(serialized_graph_data, step_count) - - path_data = { - 'strongest_path': strongest_path, - 'path_weights': path_weights, - 'avg_similarity': avg_similarity - } if strongest_path is not None else None - - yield f"data: {json.dumps({'type': 'final', 'content': final_answer, 'graph': serialized_graph_data, 'path_data': path_data})}\n\n" - - steps.append(("Final Answer", final_answer, thinking_time)) - - yield f"data: {json.dumps({'type': 'done', 'total_time': total_thinking_time})}\n\n" - - # Stop processing here - return - -def clear_database(conn): - c = conn.cursor() - c.execute("DELETE FROM embeddings") - conn.commit() - -@app.route('/') -def index(): - return render_template('index.html') - -@app.route('/query', methods=['GET', 'POST']) -def query(): - if request.method == 'POST': - user_query = request.json['query'] - else: # GET - user_query = request.args.get('query') - - if not user_query: - return jsonify({"error": "No query provided"}), 400 - - conn = create_database() - - # Clear the database before processing the new query - clear_database(conn) - - # Add user query to database - query_embedding = get_embedding(user_query) - insert_data(conn, user_query, query_embedding, True) - - def generate(): - yield from generate_response(user_query, conn) - - # Rebuild Annoy index after adding new data - build_annoy_index(conn) - - # Find similar questions/answers - similar_items = find_similar(conn, query_embedding, top_k=5) - yield f"data: {json.dumps({'type': 'similar', 'items': similar_items})}\n\n" - - conn.close() - - return Response(generate(), mimetype='text/event-stream') - -def check_consistency(final_answer, evaluation): - #messages = [ - # {"role": "system", "content": "You are a consistency checker. Compare the final answer and the evaluation, and determine if they are consistent or if the evaluation suggests a significantly different answer."}, - # {"role": "user", "content": f"Final answer: {final_answer}\n\nEvaluation: {evaluation}\n\nAre these consistent? Respond with ONLY 'consistent' or 'inconsistent'."} - #] - # - #for attempt in range(5): # Try up to 5 times - # response = "" - # for chunk in stream_api_call(messages, 50): - # response += chunk - # - # response = response.strip().lower() - # print(f"check_consistency response (attempt {attempt + 1}):", response) - # - # if response.startswith("consistent") or response.startswith("inconsistent"): - # return response.startswith("consistent") - # - # # If we reach here, the response was invalid, so we'll try again - # messages.append({"role": "user", "content": "Please respond with 'consistent' or 'inconsistent' at the beginning."}) - # - ## If we've tried 5 times and still haven't got a valid response, default to inconsistent - #print("Failed to get a valid consistency check after 5 attempts. Defaulting to inconsistent.") - #return False - return True - -if __name__ == '__main__': - app.run(host='0.0.0.0', port=5100, debug=True) \ No newline at end of file +if __name__ == "__main__": + run() diff --git a/docs/claims/edge_spread.json b/docs/claims/edge_spread.json new file mode 100644 index 0000000..afed938 --- /dev/null +++ b/docs/claims/edge_spread.json @@ -0,0 +1,105 @@ +{ + "provenance": "measured", + "measured_at": "2026-08-10T10:03:51+00:00", + "topics": [ + "arithmetic", + "biology", + "cities", + "logic" + ], + "steps_per_topic": 6, + "nomic-embed-text": { + "model": "nomic-embed-text:latest", + "dim": 768, + "topics": [ + { + "n": 9, + "mean": 0.6292892297108968, + "std": 0.09464406383583951, + "cv": 0.1503983532013096, + "min": 0.5430697798728943, + "max": 0.8689034581184387, + "topic": "cities" + }, + { + "n": 9, + "mean": 0.6608760290675693, + "std": 0.09704783583442879, + "cv": 0.14684726267247686, + "min": 0.5217607617378235, + "max": 0.8333709836006165, + "topic": "arithmetic" + }, + { + "n": 9, + "mean": 0.642390059100257, + "std": 0.08941101911429446, + "cv": 0.1391849357686592, + "min": 0.5394383668899536, + "max": 0.7876884937286377, + "topic": "biology" + }, + { + "n": 9, + "mean": 0.731500334209866, + "std": 0.07002527629005542, + "cv": 0.09572829022107501, + "min": 0.6637407541275024, + "max": 0.8580729961395264, + "topic": "logic" + } + ], + "n": 36, + "cv": 0.13303971046588017, + "cv_stdev": 0.02531058309785679, + "mean": 0.6660139130221473, + "mean_stdev": 0.04553993869832083 + }, + "all-minilm": { + "model": "all-minilm:latest", + "dim": 384, + "topics": [ + { + "n": 9, + "mean": 0.403360805577702, + "std": 0.12544568684941299, + "cv": 0.3110011808652231, + "min": 0.2296208292245865, + "max": 0.7063212990760803, + "topic": "cities" + }, + { + "n": 9, + "mean": 0.4838050603866577, + "std": 0.12142483143977993, + "cv": 0.2509788371017389, + "min": 0.2908048927783966, + "max": 0.72198885679245, + "topic": "arithmetic" + }, + { + "n": 9, + "mean": 0.4607340412007438, + "std": 0.1862419171012844, + "cv": 0.4042286882382498, + "min": 0.2195516973733902, + "max": 0.7271787524223328, + "topic": "biology" + }, + { + "n": 9, + "mean": 0.5674395660559336, + "std": 0.07905256042341419, + "cv": 0.13931450175897997, + "min": 0.4771464467048645, + "max": 0.6971835494041443, + "topic": "logic" + } + ], + "n": 36, + "cv": 0.276380801991048, + "cv_stdev": 0.1110196426025803, + "mean": 0.4788348683052593, + "mean_stdev": 0.06806729356375978 + } +} diff --git a/docs/claims/layer_sweep.json b/docs/claims/layer_sweep.json new file mode 100644 index 0000000..aa5949b --- /dev/null +++ b/docs/claims/layer_sweep.json @@ -0,0 +1,44 @@ +{ + "provenance": "measured", + "measured_at": "2026-08-10T10:46:38+00:00", + "model": "HuggingFaceTB/SmolLM2-135M", + "n_blocks": 30, + "dim": 576, + "pooling": "last", + "final_norm_applied": true, + "layers": { + "blocks.0": { + "within_topic": 0.9976432065169016, + "across_topic": 0.9957032010511115, + "separation": 0.0019400054657900956, + "mean_abs": 0.9961249232292175 + }, + "blocks.7": { + "within_topic": 0.8954060484965642, + "across_topic": 0.8336415079732736, + "separation": 0.06176454052329061, + "mean_abs": 0.8470685482025146 + }, + "blocks.15": { + "within_topic": 0.9141085694233576, + "across_topic": 0.8629755915866958, + "separation": 0.05113297783666182, + "mean_abs": 0.8740914463996887 + }, + "blocks.22": { + "within_topic": 0.8931873053312301, + "across_topic": 0.7799073490831587, + "separation": 0.11327995624807141, + "mean_abs": 0.8045334219932556 + }, + "blocks.29": { + "within_topic": 0.9263378192981084, + "across_topic": 0.7784764518340429, + "separation": 0.14786136746406553, + "mean_abs": 0.8106202483177185 + } + }, + "best_layer": "blocks.29", + "best_separation": 0.14786136746406553, + "first_layer_separation": 0.0019400054657900956 +} diff --git a/docs/claims/search_bench.json b/docs/claims/search_bench.json new file mode 100644 index 0000000..c7f95a7 --- /dev/null +++ b/docs/claims/search_bench.json @@ -0,0 +1,12 @@ +{ + "provenance": "measured", + "machine": "Darwin arm64 python3.12.12", + "dim": 768, + "top_k": 5, + "all_pairs_20_steps_ms": 0.02154102548956871, + "exact_topk_1k_ms": 0.017042038962244987, + "exact_topk_100k_ms": 3.289832966402173, + "annoy_returns_self_first": false, + "annoy_returns_k_results": 1, + "annoy_version": "1.17.3" +} diff --git a/docs/example.png b/docs/example.png new file mode 100644 index 0000000..c78e8f4 Binary files /dev/null and b/docs/example.png differ diff --git a/example.png b/example.png deleted file mode 100644 index 28cb19b..0000000 Binary files a/example.png and /dev/null differ diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..aae9741 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,76 @@ +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +name = "mpe-lkg" +version = "0.2.0" +description = "Local Knowledge Graph: a local LLM reasons step by step, and the steps become a graph." +readme = "README.md" +requires-python = ">=3.10" +license = { text = "MIT" } +authors = [{ name = "Morten Punnerud-Engelstad" }] +keywords = ["llm", "ollama", "knowledge-graph", "embeddings", "reasoning", "visualization"] +classifiers = [ + "Development Status :: 4 - Beta", + "Framework :: Flask", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Topic :: Scientific/Engineering :: Artificial Intelligence", + "Topic :: Scientific/Engineering :: Visualization", +] + +# Pure Python, so the wheel is py3-none-any: one artefact for Linux, macOS and +# Windows, with nothing to compile at install time. +dependencies = [ + "Flask>=3.0", + "numpy>=1.24", + "networkx>=3.0", + "requests>=2.28", +] + +[project.optional-dependencies] +# Reading embeddings from inside a model. Heavy, and genuinely optional. +layers = ["torch>=2.1", "transformers>=4.44"] +dev = ["pytest>=8.0", "playwright>=1.48", "ruff>=0.6"] + +[project.urls] +Homepage = "https://github.com/punnerud/Local_Knowledge_Graph" +Issues = "https://github.com/punnerud/Local_Knowledge_Graph/issues" + +[project.scripts] +mpe-lkg = "mpe_lkg.cli:main" + +[tool.hatch.build.targets.wheel] +packages = ["src/mpe_lkg"] + +[tool.hatch.build.targets.sdist] +include = ["src/mpe_lkg", "tests", "scripts", "README.md", "Makefile", "pyproject.toml"] + +[tool.pytest.ini_options] +testpaths = ["tests"] +pythonpath = ["src"] +markers = [ + "ollama: needs a running Ollama with the models named in the README", + "hf: needs torch, transformers and a downloadable Hugging Face model", + "browser: needs a Playwright browser", +] +filterwarnings = ["error::DeprecationWarning"] + +[tool.ruff] +line-length = 110 +target-version = "py310" + +[tool.ruff.lint] +select = ["E", "F", "W", "I", "UP", "B", "C4", "SIM"] +ignore = ["E501"] + +[tool.ruff.lint.per-file-ignores] +"tests/*" = ["E402"] +"app.py" = ["E402"] diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 1404439..0000000 --- a/requirements.txt +++ /dev/null @@ -1,6 +0,0 @@ -Flask -numpy -scikit-learn -annoy -networkx -requests \ No newline at end of file diff --git a/scripts/__init__.py b/scripts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/scripts/bench_search.py b/scripts/bench_search.py new file mode 100644 index 0000000..5dde49d --- /dev/null +++ b/scripts/bench_search.py @@ -0,0 +1,181 @@ +#!/usr/bin/env python3 +"""Where does an approximate index start beating an exact scan, here? + +Two different shapes of work happen in this app and they do not have the same +answer, so both are measured: + +* all-pairs inside one reasoning chain -- every step against every earlier step, + which is what draws the edges. N is the number of steps, so tens. +* top-k against the whole store -- one query vector against everything ever + saved, which is what fills the "Related Questions" panel. N grows without + bound if the store is not wiped between questions. + +Also measures recall, because an approximate index that is faster and wrong is +not faster. + + .venv/bin/python scripts/bench_search.py +""" + +from __future__ import annotations + +import sys +import time + +import numpy as np + +try: + from annoy import AnnoyIndex +except ImportError: # pragma: no cover - optional benchmark dependency + AnnoyIndex = None + +DIM = 768 +SIZES = [100, 1_000, 10_000, 100_000] +TOP_K = 5 +N_TREES = 10 + + +def timed(fn, repeats: int = 5) -> float: + """Best-of-N wall clock in milliseconds.""" + best = float("inf") + for _ in range(repeats): + start = time.perf_counter() + fn() + best = min(best, time.perf_counter() - start) + return best * 1000 + + +def make_vectors(n: int, seed: int = 0) -> np.ndarray: + rng = np.random.default_rng(seed) + matrix = rng.standard_normal((n, DIM)).astype(np.float32) + return matrix / np.linalg.norm(matrix, axis=1, keepdims=True) + + +def numpy_topk(store: np.ndarray, query: np.ndarray, k: int) -> np.ndarray: + scores = store @ query + # argpartition is O(n); a full sort would be O(n log n) for no reason. + top = np.argpartition(-scores, min(k, len(scores) - 1))[:k] + return top[np.argsort(-scores[top])] + + +def bench_all_pairs() -> None: + print("\nAll-pairs similarity inside one reasoning chain") + print(f"{'steps':>8} {'numpy full matrix':>20}") + for n in (10, 20, 50, 100): + vectors = make_vectors(n) + ms = timed(lambda v=vectors: v @ v.T) + print(f"{n:>8} {ms:>17.3f} ms") + + +def bench_topk() -> None: + print("\nTop-k against the whole store") + header = f"{'vectors':>9} {'numpy exact':>12} {'annoy build':>12} {'annoy query':>12} {'annoy recall':>12}" + print(header) + + for n in SIZES: + store = make_vectors(n) + query = make_vectors(1, seed=99)[0] + + numpy_ms = timed(lambda s=store, q=query: numpy_topk(s, q, TOP_K)) + exact = set(numpy_topk(store, query, TOP_K).tolist()) + + if AnnoyIndex is None: + print(f"{n:>9} {numpy_ms:>9.3f} ms {'(annoy not installed)':>40}") + continue + + def build(s=store): + index = AnnoyIndex(DIM, "angular") + for i, row in enumerate(s): + index.add_item(i, row) + index.build(N_TREES) + return index + + build_ms = timed(build, repeats=1) + index = build() + query_ms = timed(lambda idx=index, q=query: idx.get_nns_by_vector(q, TOP_K)) + approx = set(index.get_nns_by_vector(query, TOP_K)) + recall = len(exact & approx) / len(exact) + + print( + f"{n:>9} {numpy_ms:>9.3f} ms {build_ms:>9.1f} ms {query_ms:>9.3f} ms {recall:>11.0%}" + ) + + +def bench_amortised() -> None: + """The index has to be rebuilt whenever a vector is added. + + This app appends after every reasoning step, so the build cost is not paid + once -- it is paid again on every insert unless the index is kept incremental, + which Annoy cannot do: an Annoy index is immutable once built. + """ + if AnnoyIndex is None: + return + print("\nCost of one insert followed by one query (Annoy must rebuild)") + print(f"{'vectors':>9} {'numpy exact':>12} {'annoy rebuild+query':>21}") + for n in SIZES: + store = make_vectors(n) + query = make_vectors(1, seed=99)[0] + numpy_ms = timed(lambda s=store, q=query: numpy_topk(s, q, TOP_K)) + + def rebuild_and_query(s=store, q=query): + index = AnnoyIndex(DIM, "angular") + for i, row in enumerate(s): + index.add_item(i, row) + index.build(N_TREES) + return index.get_nns_by_vector(q, TOP_K) + + annoy_ms = timed(rebuild_and_query, repeats=1) + print(f"{n:>9} {numpy_ms:>9.3f} ms {annoy_ms:>18.1f} ms") + + +def write_claims() -> None: + """Record the numbers the README quotes, so they can be checked rather than trusted.""" + import json + import pathlib + import platform + + store_1k = make_vectors(1_000) + store_100k = make_vectors(100_000) + query = make_vectors(1, seed=99)[0] + chain = make_vectors(20) + + claims = { + "provenance": "measured", + "machine": f"{platform.system()} {platform.machine()} python{platform.python_version()}", + "dim": DIM, + "top_k": TOP_K, + "all_pairs_20_steps_ms": timed(lambda: chain @ chain.T), + "exact_topk_1k_ms": timed(lambda: numpy_topk(store_1k, query, TOP_K)), + "exact_topk_100k_ms": timed(lambda: numpy_topk(store_100k, query, TOP_K)), + } + + if AnnoyIndex is not None: + # Does the approximate index return the right answer at all? A vector's + # nearest neighbour is itself, at distance zero. On this platform the + # installed build fails that, which is why it is not a dependency. + probe = make_vectors(100) + index = AnnoyIndex(DIM, "angular") + for i, row in enumerate(probe): + index.add_item(i, row) + index.build(N_TREES) + neighbours = index.get_nns_by_item(7, TOP_K) + claims["annoy_returns_self_first"] = bool(neighbours and neighbours[0] == 7) + claims["annoy_returns_k_results"] = len(neighbours) + claims["annoy_version"] = "1.17.3" + + out = pathlib.Path(__file__).resolve().parent.parent / "docs" / "claims" / "search_bench.json" + out.parent.mkdir(parents=True, exist_ok=True) + out.write_text(json.dumps(claims, indent=2) + "\n") + print(f"\nwrote {out.name}") + + +def main() -> int: + print(f"dim={DIM} top_k={TOP_K} n_trees={N_TREES} numpy={np.__version__}") + bench_all_pairs() + bench_topk() + bench_amortised() + write_claims() + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/scripts/check_numbers.py b/scripts/check_numbers.py new file mode 100644 index 0000000..c956050 --- /dev/null +++ b/scripts/check_numbers.py @@ -0,0 +1,251 @@ +#!/usr/bin/env python3 +"""Fail loudly when a documented number stops being true. + +Prose drifts away from data silently. This turns every measurable claim in the +README into a lookup against the JSON that produced it, so a claim that stops +holding breaks the build instead of quietly becoming a lie. + +Four rules the checks below follow: + +1. The claim's English wording is the check's label, so a failure reads as a + sentence rather than as a key path. +2. Tolerances come from observed spread, not from taste. +3. Some checks must measure against something other than this pipeline's own + history -- a synthetic input whose answer is known by arithmetic. A consistency + gate cannot detect a consistent error. +4. Generated files carry a provenance field which defaults to the untrusted value, + so an old file is never mistaken for a fresh measurement. +""" + +from __future__ import annotations + +import json +import math +import pathlib +import sys + +ROOT = pathlib.Path(__file__).resolve().parent.parent +sys.path.insert(0, str(ROOT / "src")) +sys.path.insert(0, str(ROOT)) + +import numpy as np # noqa: E402 + +from mpe_lkg.graph import cosine_similarity, strongest_path, top_similarities # noqa: E402 + +CLAIMS = "docs/claims/edge_spread.json" +SEARCH = "docs/claims/search_bench.json" +SWEEP = "docs/claims/layer_sweep.json" + +# (file, extractor, expected, tolerance, label) +# +# Tolerances are three times the spread observed across the four independent topics +# in scripts/measure.py, so these fail on a regression rather than on the ordinary +# variation between one topic and the next. The per-topic standard deviations are +# recorded in the same file as cv_stdev / mean_stdev. +FILE_CHECKS = [ + ( + CLAIMS, + lambda d: 1.0 if d.get("provenance") == "measured" else 0.0, + 1.0, + 0.001, + "edge spread is measured, not inherited", + ), + ( + CLAIMS, + lambda d: float(d["all-minilm"]["dim"]), + 384.0, + 0.0, + "all-minilm reports 384 dimensions", + ), + ( + CLAIMS, + lambda d: float(d["nomic-embed-text"]["dim"]), + 768.0, + 0.0, + "nomic-embed-text reports 768 dimensions", + ), + ( + CLAIMS, + lambda d: float(d["all-minilm"]["n"]), + 36.0, + 0.0, + "four six-step topics at top_k=2 give 36 edges", + ), + ( + CLAIMS, + lambda d: float(d["nomic-embed-text"]["mean"]), + 0.6660, + 0.14, + "reasoning steps average 0.67 cosine under nomic-embed-text", + ), + ( + CLAIMS, + lambda d: float(d["nomic-embed-text"]["cv"]), + 0.1330, + 0.076, + "nomic-embed-text edge weights vary by only 13 percent of their mean", + ), + ( + CLAIMS, + lambda d: float(d["all-minilm"]["mean"]), + 0.4788, + 0.20, + "reasoning steps average 0.48 cosine under all-minilm", + ), + ( + CLAIMS, + lambda d: float(d["all-minilm"]["cv"]), + 0.2764, + 0.33, + "all-minilm edge weights vary by 28 percent of their mean", + ), + ( + CLAIMS, + # The comparison, not either number alone: the larger, better-regarded model + # produces the *less* discriminative graph on this task. If that ever + # reverses, the advice in the README is wrong and should change. + lambda d: 1.0 if d["nomic-embed-text"]["cv"] < d["all-minilm"]["cv"] else 0.0, + 1.0, + 0.001, + "nomic-embed-text separates steps less than all-minilm does", + ), + # Timings are machine-dependent, so these are upper bounds rather than point + # values: expected 0 with the bound as the tolerance. They are set an order of + # magnitude above what this machine measures, so they fail on a complexity + # regression -- an accidental O(n log n) sort or a per-query database decode -- + # rather than on a slower laptop. + ( + SEARCH, + lambda d: float(d["all_pairs_20_steps_ms"]), + 0.0, + 1.0, + "all-pairs over a 20-step chain stays under a millisecond", + ), + ( + SEARCH, + lambda d: float(d["exact_topk_1k_ms"]), + 0.0, + 1.0, + "exact top-k over 1000 vectors stays under a millisecond", + ), + ( + SEARCH, + lambda d: float(d["exact_topk_100k_ms"]), + 0.0, + 50.0, + "exact top-k over 100000 vectors stays under 50 ms", + ), + ( + SEARCH, + # Falsified-prediction guard. The approximate index this project used to + # depend on returns one wrong neighbour per query on a current numpy, which + # is why it was removed. If a future build starts behaving, that is news and + # the removal is worth revisiting. + lambda d: 1.0 if d.get("annoy_returns_self_first") else 0.0, + 0.0, + 0.001, + "annoy 1.17.3 still fails to return a vector as its own neighbour", + ), + ( + SWEEP, + lambda d: 1.0 if d.get("provenance") == "measured" else 0.0, + 1.0, + 0.001, + "layer sweep is measured, not inherited", + ), + ( + SWEEP, + # The first block sees each token before any context has been mixed in, so it + # cannot tell one topic from another. Near-zero here is the control that says + # the separation measured deeper in is real and not an artefact of the metric. + lambda d: float(d["first_layer_separation"]), + 0.0, + 0.02, + "the first block separates topics by almost nothing", + ), + ( + SWEEP, + lambda d: float(d["best_separation"]), + 0.148, + 0.06, + "the deepest block separates topics by about 0.15", + ), + ( + SWEEP, + lambda d: 1.0 if d["best_separation"] > 5 * max(d["first_layer_separation"], 1e-6) else 0.0, + 1.0, + 0.001, + "depth separates topics several times better than the first block", + ), +] + + +def ground_truth_checks() -> list[tuple[bool, str, str]]: + """Checks whose expected value comes from arithmetic, not from a previous run.""" + results = [] + + # Two unit vectors 60 degrees apart have cosine exactly 0.5. + a = np.array([1.0, 0.0], dtype=np.float32) + b = np.array([0.5, math.sqrt(3) / 2], dtype=np.float32) + got = cosine_similarity(a, b) + results.append((abs(got - 0.5) < 1e-6, f"{got:.6f} vs 0.5", "cosine of a 60-degree pair is exactly 0.5")) + + # Orthogonal vectors are exactly 0. + got = cosine_similarity(np.array([1.0, 0.0]), np.array([0.0, 1.0])) + results.append((abs(got) < 1e-6, f"{got:.6f} vs 0.0", "cosine of an orthogonal pair is exactly 0")) + + # top_similarities must rank by the analytic ordering, looking only backwards. + vectors = np.array([[1, 0], [0, 1], [math.cos(0.1), math.sin(0.1)]], dtype=np.float32) + order = [index for index, _ in top_similarities(vectors, 2, top_k=2)] + results.append((order == [0, 1], f"{order} vs [0, 1]", "nearest earlier step is ranked first")) + + # The strongest path is the one whose similarities multiply highest: a chain of + # three 0.9 edges (0.729) beats a single 0.10 shortcut. A greedy search over + # negated weights returns the shortcut instead. + graph = { + "nodes": [{"id": n} for n in ("A", "B", "C", "D")], + "edges": [ + {"from": "A", "to": "D", "value": 0.10}, + {"from": "A", "to": "B", "value": 0.90}, + {"from": "B", "to": "C", "value": 0.90}, + {"from": "C", "to": "D", "value": 0.90}, + ], + } + path, _, mean = strongest_path(graph, "A", "D") + results.append( + (path == ["A", "B", "C", "D"], f"{path}", "strongest path maximises the product of similarities") + ) + results.append((abs(mean - 0.9) < 1e-6, f"{mean:.6f} vs 0.9", "path similarity is the geometric mean")) + + return results + + +def main() -> int: + failures = skipped = 0 + + for passed, detail, label in ground_truth_checks(): + print(f"{'ok ' if passed else 'FAIL'} {label:<52} {detail}") + failures += not passed + + for path, extract, expected, tolerance, label in FILE_CHECKS: + target = ROOT / path + if not target.exists(): + print(f"SKIP {label:<52} ({path} missing -- run 'make measure')") + skipped += 1 + continue + try: + got = float(extract(json.loads(target.read_text()))) + except (KeyError, TypeError, ValueError) as exc: + print(f"FAIL {label:<52} could not read: {exc}") + failures += 1 + continue + bad = abs(got - expected) > tolerance + print(f"{'FAIL' if bad else 'ok '} {label:<52} {got:>10.4f} expected {expected} +/- {tolerance}") + failures += bad + + print(f"\n{failures} failed, {skipped} skipped") + return 1 if failures else 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/layer_sweep.py b/scripts/layer_sweep.py new file mode 100644 index 0000000..5bee22a --- /dev/null +++ b/scripts/layer_sweep.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python3 +"""How well does each layer of a model separate one topic from another? + +The point of reading inside a model is that "the embedding" is not one thing. This +sweeps a set of layers over the same fixed reasoning-step corpus and reports, per +layer, how far apart it puts steps from the same topic versus steps from different +topics. That separation is the quantity that decides whether a graph drawn from +that layer means anything. + +The corpus is the one in scripts/measure.py: four unrelated topics of six steps +each. Same texts through every layer, so the number describes the layer. + + .venv/bin/python scripts/layer_sweep.py [model] [layer ...] +""" + +from __future__ import annotations + +import json +import pathlib +import statistics +import sys +from datetime import datetime, timezone + +ROOT = pathlib.Path(__file__).resolve().parent.parent +sys.path.insert(0, str(ROOT / "src")) +sys.path.insert(0, str(ROOT)) + +import numpy as np # noqa: E402 + +from scripts.measure import TOPICS # noqa: E402 + +DEFAULT_MODEL = "HuggingFaceTB/SmolLM2-135M" + + +def separation(vectors: np.ndarray, labels: list[str]) -> dict: + """Mean within-topic similarity minus mean across-topic similarity. + + A layer that scores near zero is not distinguishing the topics at all, whatever + its absolute similarities look like. + """ + similarity = vectors @ vectors.T + same, different = [], [] + for i in range(len(labels)): + for j in range(i + 1, len(labels)): + (same if labels[i] == labels[j] else different).append(float(similarity[i, j])) + + within = statistics.fmean(same) + across = statistics.fmean(different) + return { + "within_topic": within, + "across_topic": across, + "separation": within - across, + "mean_abs": float(np.abs(similarity[np.triu_indices(len(labels), 1)]).mean()), + } + + +def main() -> int: + model_name = sys.argv[1] if len(sys.argv) > 1 else DEFAULT_MODEL + requested = sys.argv[2:] + + try: + from mpe_lkg.layers import MultiLayerProbe, describe_layers + except ImportError as exc: + print(exc) + return 1 + + texts, labels = [], [] + for topic, steps in TOPICS.items(): + texts.extend(steps) + labels.extend([topic] * len(steps)) + + if not requested: + from mpe_lkg.layers import HiddenStateEmbedding + + probe = HiddenStateEmbedding(model_name, layer="blocks.-1") + n = describe_layers(probe.model)["n_blocks"] + # Evenly spaced through the stack, always including the first and the last. + requested = [f"blocks.{i}" for i in sorted({0, n // 4, n // 2, 3 * n // 4, n - 1})] + del probe + + print(f"model={model_name} texts={len(texts)} topics={len(TOPICS)}") + multi = MultiLayerProbe(model_name, requested) + info = describe_layers(multi.probes[requested[0]].model) + print(f"block stack: {info['block_stack']} ({info['n_blocks']} x {info['block_type']}), " + f"final norm applied: {info['has_final_norm']}\n") + + results = {} + print(f"{'layer':>12} {'within':>8} {'across':>8} {'separation':>11}") + for layer, vectors in multi.embed(texts).items(): + stats = separation(vectors, labels) + results[layer] = stats + print(f"{layer:>12} {stats['within_topic']:>8.4f} {stats['across_topic']:>8.4f} " + f"{stats['separation']:>11.4f}") + + best = max(results, key=lambda k: results[k]["separation"]) + print(f"\nbest separation: {best} ({results[best]['separation']:.4f})") + + payload = { + "provenance": "measured", + "measured_at": datetime.now(timezone.utc).isoformat(timespec="seconds"), + "model": model_name, + "n_blocks": info["n_blocks"], + "dim": int(multi.probes[requested[0]].dim), + "pooling": multi.probes[requested[0]].pooling, + "final_norm_applied": info["has_final_norm"], + "layers": results, + "best_layer": best, + "best_separation": results[best]["separation"], + "first_layer_separation": results[requested[0]]["separation"], + } + out = ROOT / "docs" / "claims" / "layer_sweep.json" + out.parent.mkdir(parents=True, exist_ok=True) + out.write_text(json.dumps(payload, indent=2) + "\n") + print(f"wrote {out.relative_to(ROOT)}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/measure.py b/scripts/measure.py new file mode 100644 index 0000000..aeed031 --- /dev/null +++ b/scripts/measure.py @@ -0,0 +1,132 @@ +#!/usr/bin/env python3 +"""Measure how much the drawn edge weights actually vary, per embedding model. + +The graph draws an edge for every pair of reasoning steps it thinks are related, +and labels it with a cosine similarity. If those similarities all land in a narrow +band the picture looks informative while carrying almost nothing, so this measures +the spread rather than assuming it. + +Deliberately not driven by a live language model: the same fixed step texts go +through every embedding model, so the number describes the embedder and not the +sampling noise of whatever wrote the steps. Several independent topics are measured +so the observed spread across them can set the tolerance in check_numbers.py -- +a single run would give a number with no error bar. + +Writes docs/claims/edge_spread.json. Run with: make measure +""" + +from __future__ import annotations + +import json +import pathlib +import statistics +import sys +from datetime import datetime, timezone + +ROOT = pathlib.Path(__file__).resolve().parent.parent +sys.path.insert(0, str(ROOT / "src")) +sys.path.insert(0, str(ROOT)) + +from mpe_lkg import backends # noqa: E402 +from mpe_lkg.graph import build_graph, edge_weight_spread # noqa: E402 + +# Four unrelated topics, each a plausible chain of reasoning steps. +TOPICS = { + "cities": [ + "First I need to decide whether the question means city proper or metropolitan area.", + "Using city proper populations, the largest are Tokyo, Delhi, Shanghai, Dhaka and Sao Paulo.", + "Metropolitan definitions change the ranking, because they absorb surrounding municipalities.", + "I should check whether the figures are recent, since urban populations move quickly.", + "Sources disagree by several million for Delhi depending on the boundary used.", + "Taking city proper and recent UN figures, Tokyo remains the largest.", + ], + "arithmetic": [ + "The problem asks for the product of two three-digit numbers.", + "I will decompose 347 times 216 into partial products to reduce mistakes.", + "347 times 200 is 69400, and 347 times 16 is 5552.", + "Adding the partial products gives 74952.", + "Checking by estimation, 350 times 216 is about 75600, which is close.", + "The product is 74952.", + ], + "biology": [ + "Photosynthesis converts light energy into chemical energy stored in glucose.", + "The light dependent reactions happen in the thylakoid membrane and produce ATP and NADPH.", + "The Calvin cycle then fixes carbon dioxide using that ATP and NADPH.", + "An alternative framing would separate the oxygen evolving complex as its own stage.", + "Temperature and light intensity both limit the overall rate.", + "So photosynthesis is a two stage process linked by ATP and NADPH.", + ], + "logic": [ + "The statement is a conditional, so its truth depends only on the case where the premise holds.", + "I should test whether the converse is being assumed anywhere in the argument.", + "Affirming the consequent would be a fallacy here, and the argument appears to do that.", + "Trying a counterexample: the premise can be false while the conclusion is true.", + "That counterexample shows the inference is not valid.", + "The argument is invalid because it affirms the consequent.", + ], +} + + +def measure(backend) -> dict: + per_topic = [] + for name, steps in TOPICS.items(): + vectors = backend.embed(steps) + ids = [f"Step{i + 1}" for i in range(len(steps))] + graph = build_graph(ids, ids, vectors, top_k=2) + spread = edge_weight_spread(graph) + spread["topic"] = name + per_topic.append(spread) + + cvs = [t["cv"] for t in per_topic] + means = [t["mean"] for t in per_topic] + return { + "model": backend.model, + "dim": backend.dim, + "topics": per_topic, + "n": sum(t["n"] for t in per_topic), + # Averaged over four independent topics, with the observed spread reported + # so a tolerance can be set from it rather than guessed. + "cv": statistics.fmean(cvs), + "cv_stdev": statistics.stdev(cvs), + "mean": statistics.fmean(means), + "mean_stdev": statistics.stdev(means), + } + + +def main() -> int: + models = [m for m in backends.list_models() if m["is_embedding"]] + if not models: + print("No embedding model installed. Try: ollama pull nomic-embed-text") + return 1 + + results = { + "provenance": "measured", + "measured_at": datetime.now(timezone.utc).isoformat(timespec="seconds"), + "topics": sorted(TOPICS), + "steps_per_topic": len(next(iter(TOPICS.values()))), + } + + for entry in models: + name = entry["name"] + print(f"measuring {name} ...", flush=True) + try: + result = measure(backends.OllamaEmbedding(name)) + except backends.BackendError as exc: + print(f" skipped: {exc}") + continue + key = name.split(":")[0] + results[key] = result + print( + f" dim={result['dim']} mean={result['mean']:.4f}+/-{result['mean_stdev']:.4f} " + f"cv={result['cv']:.4f}+/-{result['cv_stdev']:.4f} edges={result['n']}" + ) + + out = ROOT / "docs" / "claims" / "edge_spread.json" + out.parent.mkdir(parents=True, exist_ok=True) + out.write_text(json.dumps(results, indent=2) + "\n") + print(f"\nwrote {out.relative_to(ROOT)}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/screenshot.py b/scripts/screenshot.py new file mode 100644 index 0000000..56da991 --- /dev/null +++ b/scripts/screenshot.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 +"""Drive the real app in a real browser and save a screenshot. + +Used to regenerate example.png, and as a manual end-to-end check against a live +model rather than the fakes the test suite uses. + + .venv/bin/python scripts/screenshot.py "What is the capital of France?" out.png +""" + +from __future__ import annotations + +import pathlib +import socket +import sys +import threading + +ROOT = pathlib.Path(__file__).resolve().parent.parent +sys.path.insert(0, str(ROOT / "src")) +sys.path.insert(0, str(ROOT)) + +from playwright.sync_api import sync_playwright # noqa: E402 +from werkzeug.serving import make_server # noqa: E402 + +import mpe_lkg.app as app_module # noqa: E402 + + +def free_port() -> int: + with socket.socket() as sock: + sock.bind(("127.0.0.1", 0)) + return int(sock.getsockname()[1]) + + +def main() -> int: + question = sys.argv[1] if len(sys.argv) > 1 else "Can you give the 5 biggest cities in population size in order?" + out = pathlib.Path(sys.argv[2] if len(sys.argv) > 2 else "docs/example.png") + + app_module.app.config["DB_PATH"] = str(ROOT / "embeddings.db") + port = free_port() + server = make_server("127.0.0.1", port, app_module.app, threaded=True) + thread = threading.Thread(target=server.serve_forever, daemon=True) + thread.start() + + try: + with sync_playwright() as playwright: + browser = playwright.chromium.launch() + page = browser.new_context(viewport={"width": 1600, "height": 1000}).new_page() + page.goto(f"http://127.0.0.1:{port}") + page.fill("#query", question) + page.click("#submit") + print("waiting for the model ...", flush=True) + page.wait_for_function( + "() => document.querySelector('#submit').disabled === false", timeout=600_000 + ) + page.wait_for_timeout(2500) # let the graph physics settle + + errors = page.locator(".error-box") + if errors.count(): + print(f"error shown in page: {errors.first.inner_text()}") + print(f"status: {page.locator('#status').inner_text()}") + print(f"steps: {page.locator('.step').count()}, final: {page.locator('.final-answer').count()}") + print(f"nodes: {page.evaluate('() => nodes.get().length')}, " + f"edges: {page.evaluate('() => edges.get().length')}") + + page.screenshot(path=str(out), full_page=True) + browser.close() + finally: + server.shutdown() + thread.join(timeout=5) + + print(f"wrote {out} ({out.stat().st_size // 1024} KB)") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/src/mpe_lkg/__init__.py b/src/mpe_lkg/__init__.py new file mode 100644 index 0000000..e583a15 --- /dev/null +++ b/src/mpe_lkg/__init__.py @@ -0,0 +1,36 @@ +"""Local Knowledge Graph — a local LLM reasons step by step, and the steps become a graph. + + pip install mpe-lkg + mpe-lkg + +Or from Python: + + from mpe_lkg import create_app + create_app().run(port=5100) +""" + +from __future__ import annotations + +__version__ = "0.2.0" + +__all__ = ["__version__", "create_app", "main", "health"] + + +def create_app(): + """The Flask application. Imported lazily so `import mpe_lkg` stays cheap.""" + from .app import app + + return app + + +def main(argv: list[str] | None = None) -> int: + from .cli import main as _main + + return _main(argv) + + +def health(base_url: str | None = None) -> dict: + """Is Ollama reachable, and does it have what this needs?""" + from . import backends + + return backends.health(base_url or backends.DEFAULT_BASE_URL) diff --git a/src/mpe_lkg/__main__.py b/src/mpe_lkg/__main__.py new file mode 100644 index 0000000..41c9497 --- /dev/null +++ b/src/mpe_lkg/__main__.py @@ -0,0 +1,6 @@ +"""``python -m mpe_lkg``.""" + +from .cli import main + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/src/mpe_lkg/app.py b/src/mpe_lkg/app.py new file mode 100644 index 0000000..bf10860 --- /dev/null +++ b/src/mpe_lkg/app.py @@ -0,0 +1,169 @@ +"""Local Knowledge Graph -- Flask entry point. + +Thin by design: routing, server-sent-event framing, and process configuration. The +model backends live in ``backends.py``, the graph maths in ``graph.py``, the storage +in ``store.py``, and the reasoning loop in ``reasoning.py``. +""" + +from __future__ import annotations + +import json +import os +import queue +import threading + +from flask import Flask, Response, jsonify, render_template, request + +from . import backends +from .reasoning import reason +from .store import EmbeddingStore + +app = Flask(__name__) + +DB_PATH = os.environ.get("LKG_DB", "embeddings.db") +# How long to wait for the next event before telling the browser we are still alive. +HEARTBEAT_SECONDS = float(os.environ.get("LKG_HEARTBEAT", "5")) + + +def _sse(event: dict) -> str: + return f"data: {json.dumps(event)}\n\n" + + +def make_backends() -> tuple: + """Build the default backends. Tests replace this. + + Set LKG_EMBED_BACKEND=hf to embed from inside a local model instead of from an + embedding endpoint, which is what makes a model with no embedding API usable and + lets you point at a specific depth: + + LKG_EMBED_BACKEND=hf LKG_HF_MODEL=HuggingFaceTB/SmolLM2-135M \ + LKG_HF_LAYER=blocks.-1 python app.py + """ + chat = backends.OllamaChat(backends.DEFAULT_CHAT_MODEL) + + if os.environ.get("LKG_EMBED_BACKEND") == "hf": + from .layers import HiddenStateEmbedding + + return chat, HiddenStateEmbedding( + os.environ.get("LKG_HF_MODEL", "HuggingFaceTB/SmolLM2-135M"), + layer=os.environ.get("LKG_HF_LAYER", "blocks.-1"), + pooling=os.environ.get("LKG_HF_POOLING", "last"), + ) + + return chat, backends.OllamaEmbedding(backends.DEFAULT_EMBED_MODEL) + + +@app.route("/") +def index(): + return render_template("index.html") + + +@app.route("/health") +def health(): + """Why nothing is happening, in terms a user can act on.""" + return jsonify(backends.health(backends.DEFAULT_BASE_URL)) + + +@app.route("/query", methods=["GET", "POST"]) +def query(): + if request.method == "POST": + user_query = (request.json or {}).get("query", "") + else: + user_query = request.args.get("query", "") + user_query = user_query.strip() + + if not user_query: + return jsonify({"error": "No query provided"}), 400 + + chat, embedder = app.config.get("BACKENDS_FACTORY", make_backends)() + + def generate(): + """Stream events, and keep talking even while the model is thinking. + + The reasoning loop runs on a worker thread and hands events over a queue, so + this generator can emit a heartbeat during the long silence before a slow + model produces its first token. Without it a browser sees an open connection + and no data, which is indistinguishable from the app being broken. + """ + store = EmbeddingStore(app.config.get("DB_PATH", DB_PATH)) + events: queue.Queue = queue.Queue() + sentinel = object() + + def worker(): + try: + # The store deliberately accumulates across questions. It used to be + # wiped at the start of every request, which made "Related Questions + # and Answers" structurally unable to show anything but the current + # run's own steps. Set LKG_RESET_DB=1 to go back to a clean slate. + if os.environ.get("LKG_RESET_DB") == "1": + store.clear() + query_vector = embedder.embed([user_query])[0] + query_id = store.add( + user_query, + query_vector, + is_question=True, + model=embedder.describe().get("model", ""), + ) + for event in reason(user_query, chat=chat, embedder=embedder, store=store): + events.put(event) + if event["type"] == "error": + return + similar = store.find_similar( + query_vector, + top_k=5, + model=embedder.describe().get("model", ""), + exclude_ids={query_id}, + ) + events.put({"type": "similar", "items": similar, "store_size": store.count()}) + except backends.BackendError as exc: + events.put({"type": "error", "message": str(exc), "hint": exc.hint}) + except Exception as exc: # noqa: BLE001 + events.put({"type": "error", "message": f"{type(exc).__name__}: {exc}", "hint": ""}) + finally: + events.put(sentinel) + + thread = threading.Thread(target=worker, daemon=True) + thread.start() + + try: + while True: + try: + event = events.get(timeout=HEARTBEAT_SECONDS) + except queue.Empty: + yield ": heartbeat\n\n" + continue + if event is sentinel: + break + yield _sse(event) + yield _sse({"type": "done_stream"}) + finally: + thread.join(timeout=1.0) + store.close() + + return Response( + generate(), + mimetype="text/event-stream", + headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no"}, + ) + + +def run(host: str | None = None, port: int | None = None, debug: bool | None = None) -> None: + """Start the server, after saying whether the model backend is actually there.""" + status = backends.health(backends.DEFAULT_BASE_URL) + if not status["ok"]: + print(f"\n {status['problem']}\n {status['hint']}\n") + else: + print(f"\n Ollama at {status['base_url']} — models: {', '.join(status['models'])}\n") + + # Bound to localhost with the debugger off by default. The previous default of + # debug=True on 0.0.0.0 exposed the Werkzeug console to the whole network. + app.run( + host=host or os.environ.get("LKG_HOST", "127.0.0.1"), + port=port or int(os.environ.get("LKG_PORT", "5100")), + debug=os.environ.get("LKG_DEBUG", "") == "1" if debug is None else debug, + threaded=True, + ) + + +if __name__ == "__main__": + run() diff --git a/src/mpe_lkg/backends.py b/src/mpe_lkg/backends.py new file mode 100644 index 0000000..854ebee --- /dev/null +++ b/src/mpe_lkg/backends.py @@ -0,0 +1,411 @@ +"""Pluggable model backends. + +The rest of the application never talks to Ollama directly. It asks for an +``EmbeddingBackend`` and a ``ChatBackend`` and uses those, which is what makes the +reasoning loop and the graph testable without a model running anywhere. + +Two rules are load-bearing here and are easy to lose in a refactor: + +* An embedding backend reports its own dimension. Nothing downstream may assume a + size. The original code hardcoded 4096 in one place and passed it as a parameter + in another, so swapping the embedding model broke similarity search silently. +* A backend never swallows an error. A failure is raised as ``BackendError`` with a + message meant for a human, because the symptom users reported was a blank page + with nothing in the terminal. +""" + +from __future__ import annotations + +import hashlib +import json +import os +import time +from collections.abc import Iterable, Iterator +from typing import Protocol + +import numpy as np +import requests + +DEFAULT_BASE_URL = os.environ.get("OLLAMA_URL", "http://localhost:11434") +DEFAULT_CHAT_MODEL = os.environ.get("LKG_CHAT_MODEL", "llama3.1:8b") +# Empty means "look at what Ollama actually has and pick something sensible". +DEFAULT_EMBED_MODEL = os.environ.get("LKG_EMBED_MODEL", "") +REQUEST_TIMEOUT = float(os.environ.get("LKG_TIMEOUT", "120")) + +# The reasoning loop asks for exactly these three keys. Handing Ollama the schema +# means the model cannot answer with prose that fails to parse, which is where the +# "Step 5: Parsing Error" nodes in the project's own screenshot came from. +STEP_SCHEMA = { + "type": "object", + "properties": { + "title": {"type": "string"}, + "content": {"type": "string"}, + "next_action": {"type": "string", "enum": ["continue", "final_answer"]}, + }, + "required": ["title", "content", "next_action"], +} + + +class BackendError(RuntimeError): + """A model backend could not answer, with a message worth showing a user.""" + + def __init__(self, message: str, *, hint: str = "") -> None: + super().__init__(message) + self.hint = hint + + def user_message(self) -> str: + return f"{self}\n{self.hint}".strip() + + +class EmbeddingBackend(Protocol): + def embed(self, texts: Iterable[str]) -> np.ndarray: + """Return an ``(n, dim)`` float32 array of L2-normalised row vectors.""" + + @property + def dim(self) -> int: ... + + def describe(self) -> dict: ... + + +class ChatBackend(Protocol): + def stream(self, messages: list[dict], max_tokens: int, *, schema: dict | None = None) -> Iterator[str]: + """Yield response text as it arrives.""" + + def describe(self) -> dict: ... + + +def _l2_normalise(matrix: np.ndarray) -> np.ndarray: + norms = np.linalg.norm(matrix, axis=1, keepdims=True) + # A zero vector stays zero rather than becoming NaN; cosine against it is 0. + np.divide(matrix, norms, out=matrix, where=norms > 0) + return matrix + + +def _clean_for_embedding(text: str) -> str: + """Collapse whitespace so one record can never become two. + + Every text-in/vector-out endpoint that is line-oriented treats a newline as a + record separator. A single embedded newline shifts every subsequent vector onto + the wrong document, and the result looks like a plausible graph rather than an + error. Collapsing here costs nothing and removes the whole class of bug. + """ + return " ".join(text.split()) or " " + + +class OllamaEmbedding: + """Embeddings from Ollama, with the model chosen automatically if not given.""" + + def __init__( + self, + model: str = "", + *, + base_url: str = DEFAULT_BASE_URL, + fallback_model: str = DEFAULT_CHAT_MODEL, + session: requests.Session | None = None, + ) -> None: + self.base_url = base_url.rstrip("/") + self._session = session or requests.Session() + self._dim: int | None = None + self._endpoint = "/api/embed" + self.model = model or self._pick_model(fallback_model) + self.auto_selected = not model + + def _pick_model(self, fallback: str) -> str: + """Prefer a real embedding model over a chat model, if one is installed.""" + available = list_models(self.base_url, session=self._session) + embedders = [m["name"] for m in available if m.get("is_embedding")] + if embedders: + return embedders[0] + names = {m["name"] for m in available} + if fallback in names: + return fallback + # Ollama tags are "name:tag"; accept a bare name the user typed. + for name in names: + if name.split(":")[0] == fallback.split(":")[0]: + return name + return fallback + + @property + def dim(self) -> int: + if self._dim is None: + self._dim = int(self.embed(["dimension probe"]).shape[1]) + return self._dim + + def describe(self) -> dict: + return { + "kind": "ollama", + "model": self.model, + "dim": self.dim, + "base_url": self.base_url, + "auto_selected": self.auto_selected, + "endpoint": self._endpoint, + } + + def embed(self, texts: Iterable[str]) -> np.ndarray: + items = [_clean_for_embedding(t) for t in texts] + if not items: + return np.zeros((0, self._dim or 0), dtype=np.float32) + + vectors = self._embed_batch(items) + if len(vectors) != len(items): + # A silent success that returns the wrong number of rows is worse than a + # crash: every vector after the gap belongs to the wrong text. + raise BackendError( + f"Embedding model '{self.model}' returned {len(vectors)} vectors " + f"for {len(items)} inputs.", + hint="This misaligns every embedding after the gap. Retry, or pick " + "another model with LKG_EMBED_MODEL.", + ) + matrix = np.asarray(vectors, dtype=np.float32) + if matrix.ndim != 2: + raise BackendError(f"Embedding model '{self.model}' returned a malformed response.") + self._dim = int(matrix.shape[1]) + return _l2_normalise(matrix) + + def _embed_batch(self, items: list[str]) -> list[list[float]]: + try: + response = self._session.post( + f"{self.base_url}/api/embed", + json={"model": self.model, "input": items}, + timeout=REQUEST_TIMEOUT, + ) + except requests.RequestException as exc: + raise BackendError( + f"Could not reach Ollama at {self.base_url}.", + hint="Start it with 'ollama serve', or set OLLAMA_URL if it runs elsewhere.", + ) from exc + + if response.status_code == 404: + body = response.text + if "not found" in body and self.model in body: + raise BackendError( + f"Ollama does not have the embedding model '{self.model}'.", + hint=f"Install it with: ollama pull {self.model}", + ) + # Ollama before v0.3.4 has no /api/embed, only the singular endpoint. + self._endpoint = "/api/embeddings" + return [self._embed_one_legacy(text) for text in items] + + if response.status_code != 200: + raise BackendError( + f"Ollama returned HTTP {response.status_code} for an embedding request.", + hint=response.text[:400], + ) + + payload = response.json() + if payload.get("embeddings"): + return payload["embeddings"] + if payload.get("embedding"): + return [payload["embedding"]] + raise BackendError( + f"Ollama returned no embedding for model '{self.model}'.", + hint=str(payload)[:400], + ) + + def _embed_one_legacy(self, text: str) -> list[float]: + response = self._session.post( + f"{self.base_url}/api/embeddings", + json={"model": self.model, "prompt": text}, + timeout=REQUEST_TIMEOUT, + ) + if response.status_code != 200: + raise BackendError( + f"Ollama returned HTTP {response.status_code} from /api/embeddings.", + hint=response.text[:400], + ) + payload = response.json() + if not payload.get("embedding"): + raise BackendError(f"Ollama returned no embedding for model '{self.model}'.") + return payload["embedding"] + + +class OllamaChat: + """Streaming chat completions from Ollama.""" + + def __init__( + self, + model: str = DEFAULT_CHAT_MODEL, + *, + base_url: str = DEFAULT_BASE_URL, + temperature: float = 0.2, + session: requests.Session | None = None, + ) -> None: + self.model = model + self.base_url = base_url.rstrip("/") + self.temperature = temperature + self._session = session or requests.Session() + + def describe(self) -> dict: + return {"kind": "ollama", "model": self.model, "base_url": self.base_url} + + def stream(self, messages: list[dict], max_tokens: int, *, schema: dict | None = None) -> Iterator[str]: + payload = { + "model": self.model, + "messages": messages, + "stream": True, + # These belong under "options". The original code sent them at the top + # level, where Ollama ignores them -- so neither the token limit nor the + # temperature had any effect at all. + "options": {"num_predict": max_tokens, "temperature": self.temperature}, + } + if schema is not None: + payload["format"] = schema + + try: + response = self._session.post( + f"{self.base_url}/api/chat", + json=payload, + stream=True, + timeout=REQUEST_TIMEOUT, + ) + except requests.RequestException as exc: + raise BackendError( + f"Could not reach Ollama at {self.base_url}.", + hint="Start it with 'ollama serve', or set OLLAMA_URL if it runs elsewhere.", + ) from exc + + if response.status_code == 404 and self.model in response.text: + raise BackendError( + f"Ollama does not have the model '{self.model}'.", + hint=f"Install it with: ollama pull {self.model}", + ) + if response.status_code != 200: + raise BackendError( + f"Ollama returned HTTP {response.status_code} for a chat request.", + hint=response.text[:400], + ) + + produced = False + for line in response.iter_lines(): + if not line: + continue + try: + chunk = json.loads(line.decode("utf-8")) + except json.JSONDecodeError: + continue + if chunk.get("error"): + raise BackendError(f"Ollama reported an error: {chunk['error']}") + piece = chunk.get("message", {}).get("content", "") + if piece: + produced = True + yield piece + if not produced: + raise BackendError( + f"Ollama returned an empty response from model '{self.model}'.", + hint="The model may have been evicted mid-request; try again.", + ) + + +class ScriptedChat: + """A chat backend that replays canned responses. For tests.""" + + def __init__( + self, + responses: list[str], + *, + chunk_size: int = 24, + repeat_last: bool = False, + delay: float = 0.0, + ) -> None: + self._responses = list(responses) + self._chunk_size = chunk_size + self._repeat_last = repeat_last + # Lets a test observe the in-flight state of the UI, which a backend that + # answers instantly makes unobservable. + self._delay = delay + self.calls: list[list[dict]] = [] + + def describe(self) -> dict: + return {"kind": "scripted", "model": "scripted", "remaining": len(self._responses)} + + def stream(self, messages: list[dict], max_tokens: int, *, schema: dict | None = None) -> Iterator[str]: + self.calls.append(list(messages)) + if self._responses: + text = self._responses[0] if (self._repeat_last and len(self._responses) == 1) else self._responses.pop(0) + elif self._repeat_last: + text = "" + else: + raise BackendError("ScriptedChat ran out of scripted responses.") + if self._delay: + time.sleep(self._delay) + for start in range(0, len(text), self._chunk_size): + yield text[start : start + self._chunk_size] + + +class DeterministicEmbedding: + """Reproducible pseudo-embeddings derived from the text. For tests. + + Identical text yields an identical vector and similar text does not yield a + similar vector, which is exactly what a test wants: total control, no network. + """ + + def __init__(self, dim: int = 64) -> None: + self._dim = dim + + @property + def dim(self) -> int: + return self._dim + + def describe(self) -> dict: + return {"kind": "deterministic", "model": f"hash-{self._dim}", "dim": self._dim} + + def embed(self, texts: Iterable[str]) -> np.ndarray: + rows = [] + for text in texts: + seed = int.from_bytes(hashlib.sha256(_clean_for_embedding(text).encode()).digest()[:8], "big") + rows.append(np.random.default_rng(seed).standard_normal(self._dim)) + if not rows: + return np.zeros((0, self._dim), dtype=np.float32) + return _l2_normalise(np.asarray(rows, dtype=np.float32)) + + +def list_models(base_url: str = DEFAULT_BASE_URL, *, session: requests.Session | None = None) -> list[dict]: + """Return the installed Ollama models, or an empty list if it is unreachable.""" + session = session or requests + try: + response = session.get(f"{base_url.rstrip('/')}/api/tags", timeout=5) + response.raise_for_status() + payload = response.json() + except (requests.RequestException, ValueError): + return [] + + models = [] + for entry in payload.get("models", []): + name = entry.get("name", "") + caps = entry.get("capabilities") or [] + # Older Ollama builds do not report capabilities, so fall back to the naming + # convention every published embedding model follows. + is_embedding = "embedding" in caps or "embed" in name.lower() or "minilm" in name.lower() + models.append({"name": name, "is_embedding": is_embedding, "capabilities": caps}) + return models + + +def health(base_url: str = DEFAULT_BASE_URL) -> dict: + """Everything the UI needs to explain why nothing is happening.""" + models = list_models(base_url) + if not models: + return { + "ok": False, + "base_url": base_url, + "models": [], + "problem": f"No answer from Ollama at {base_url}.", + "hint": "Start it with 'ollama serve'. If it runs on another host or port, " + "set OLLAMA_URL before starting this app.", + } + + names = {m["name"] for m in models} + chat_ok = DEFAULT_CHAT_MODEL in names or any( + n.split(":")[0] == DEFAULT_CHAT_MODEL.split(":")[0] for n in names + ) + if not chat_ok: + return { + "ok": False, + "base_url": base_url, + "models": sorted(names), + "problem": f"Ollama is running but does not have the chat model '{DEFAULT_CHAT_MODEL}'.", + "hint": f"Install it with: ollama pull {DEFAULT_CHAT_MODEL}\n" + f"Or point the app at a model you already have by setting " + f"LKG_CHAT_MODEL to one of: {', '.join(sorted(names))}", + } + + return {"ok": True, "base_url": base_url, "models": sorted(names), "problem": "", "hint": ""} diff --git a/src/mpe_lkg/cli.py b/src/mpe_lkg/cli.py new file mode 100644 index 0000000..5991dc1 --- /dev/null +++ b/src/mpe_lkg/cli.py @@ -0,0 +1,56 @@ +"""The ``mpe-lkg`` command.""" + +from __future__ import annotations + +import argparse +import json +import sys + + +def build_parser() -> argparse.ArgumentParser: + from . import __version__ + + parser = argparse.ArgumentParser( + prog="mpe-lkg", + description="Local Knowledge Graph — a local LLM reasons step by step, " + "and the steps become a graph.", + epilog="Needs Ollama running locally. Run 'mpe-lkg doctor' to check.", + ) + parser.add_argument("--version", action="version", version=f"mpe-lkg {__version__}") + parser.add_argument("--host", default=None, help="default 127.0.0.1") + parser.add_argument("--port", type=int, default=None, help="default 5100") + parser.add_argument("--debug", action="store_true", help="Flask debugger; not on a shared network") + parser.add_argument( + "command", nargs="?", default="serve", choices=["serve", "doctor"], + help="serve (default) starts the web app; doctor reports what is missing", + ) + return parser + + +def doctor() -> int: + """Say exactly what is wrong and what command fixes it.""" + from . import backends + + status = backends.health(backends.DEFAULT_BASE_URL) + print(json.dumps(status, indent=2)) + if status["ok"]: + print("\nEverything the app needs is present.") + return 0 + print(f"\n{status['problem']}\n{status['hint']}", file=sys.stderr) + return 1 + + +def main(argv: list[str] | None = None) -> int: + args = build_parser().parse_args(argv) + + if args.command == "doctor": + return doctor() + + from .app import run + + run(host=args.host, port=args.port, debug=args.debug or None) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/src/mpe_lkg/graph.py b/src/mpe_lkg/graph.py new file mode 100644 index 0000000..816dbec --- /dev/null +++ b/src/mpe_lkg/graph.py @@ -0,0 +1,184 @@ +"""Graph construction and the strongest-path search. + +Pure functions over plain dicts and numpy arrays. No I/O, no Flask, no model. This +is the part the render tests pin, so it must stay free of side effects. +""" + +from __future__ import annotations + +import math + +import networkx as nx +import numpy as np + +# Similarities at or below this are treated as "no usable link". Cosine can go +# negative, and the log transform below is only defined on positive weights. +MIN_USABLE_SIMILARITY = 1e-6 + + +def cosine_similarity(a: np.ndarray, b: np.ndarray) -> float: + """Cosine similarity between two vectors, safe on zero vectors.""" + denom = float(np.linalg.norm(a) * np.linalg.norm(b)) + if denom == 0.0: + return 0.0 + return float(np.dot(a, b) / denom) + + +def similarity_matrix(vectors: np.ndarray) -> np.ndarray: + """Full pairwise cosine similarity for a stack of row vectors.""" + if len(vectors) == 0: + return np.zeros((0, 0), dtype=np.float32) + matrix = np.asarray(vectors, dtype=np.float32) + norms = np.linalg.norm(matrix, axis=1, keepdims=True) + normalised = np.divide(matrix, norms, out=np.zeros_like(matrix), where=norms > 0) + return normalised @ normalised.T + + +def top_similarities(vectors, current_index: int, top_k: int = 2) -> list[tuple[int, float]]: + """The ``top_k`` earlier vectors most similar to ``vectors[current_index]``. + + Returns ``(index, similarity)`` pairs, strongest first. Only indices strictly + before ``current_index`` are considered, because an edge always points forward + in the reasoning chain. + """ + stack = np.asarray(vectors, dtype=np.float32) + if current_index <= 0 or current_index >= len(stack): + return [] + current = stack[current_index] + scored = [(i, cosine_similarity(current, stack[i])) for i in range(current_index)] + scored.sort(key=lambda pair: pair[1], reverse=True) + return scored[:top_k] + + +def build_graph(node_ids: list[str], labels: list[str], vectors, top_k: int = 2) -> dict: + """Build the whole graph from scratch out of the current steps. + + Rebuilding rather than patching is what keeps node ids and embedding indices in + lockstep: ``node_ids[i]`` owns ``vectors[i]``, by construction, for every i. + The original code derived a node id from a separate running counter that could + skip, so an edge could point at the wrong step -- and the guard for that case + silently dropped the edge instead of reporting the mismatch. + """ + if len(node_ids) != len(labels): + raise ValueError(f"{len(node_ids)} node ids but {len(labels)} labels") + stack = np.asarray(vectors, dtype=np.float32) if len(vectors) else np.zeros((0, 0), dtype=np.float32) + if len(stack) != len(node_ids): + raise ValueError(f"{len(node_ids)} node ids but {len(stack)} vectors") + + edges: list[dict] = [] + for index in range(1, len(node_ids)): + for previous, similarity in top_similarities(stack, index, top_k=top_k): + edges.append( + { + "from": node_ids[previous], + "to": node_ids[index], + "value": float(similarity), + "length": float(300 * (1 - similarity)), + } + ) + + nodes = [] + for index, node_id in enumerate(node_ids): + connected = [e["value"] for e in edges if e["from"] == node_id or e["to"] == node_id] + value = (sum(connected) / len(connected)) * 30 + 10 if connected else 20.0 + nodes.append({"id": node_id, "label": labels[index], "value": float(value)}) + + return {"nodes": nodes, "edges": edges} + + +def serialize_graph_data(graph_data: dict) -> dict: + """Convert internal graph state into what vis.js consumes. + + ``length`` is carried through deliberately. The original serializer dropped it, + so the similarity-proportional spring length was computed on every edge and then + thrown away before it could reach the browser. + """ + return { + "nodes": [dict(node) for node in graph_data.get("nodes", [])], + "edges": [ + { + "from": edge["from"], + "to": edge["to"], + "value": float(edge["value"]), + "length": float(edge.get("length", 300 * (1 - float(edge["value"])))), + "label": f"{float(edge['value']):.2f}", + "font": {"size": 10}, + } + for edge in graph_data.get("edges", []) + ], + } + + +def strongest_path(graph_data: dict, start_node: str = "", end_node: str = "") -> tuple: + """Find the path whose similarities multiply to the largest value. + + Maximising a product of similarities is the same as minimising a sum of + ``-log(similarity)``, and those costs are non-negative, so Dijkstra applies and + the answer is exactly optimal. + + The original implementation accumulated ``cost - weight``, which makes every + effective weight negative. Dijkstra's greedy choice is invalid on negative + weights, so it returned the first path it happened to reach rather than the + strongest one, and reported that as a "weighted average". + + Returns ``(path, weights, mean_similarity)``, or ``(None, None, None)`` when no + path exists. ``mean_similarity`` is the geometric mean of the edges on the path, + which is the quantity actually being maximised. + """ + nodes = [node["id"] for node in graph_data.get("nodes", [])] + if not nodes: + return None, None, None + + start = start_node or nodes[0] + end = end_node or nodes[-1] + if start not in nodes or end not in nodes: + return None, None, None + if start == end: + return [start], [], 1.0 + + graph = nx.Graph() + graph.add_nodes_from(nodes) + for edge in graph_data.get("edges", []): + similarity = float(edge["value"]) + if similarity <= MIN_USABLE_SIMILARITY: + continue + cost = -math.log(min(similarity, 1.0)) + # Keep the strongest edge when a pair appears twice. + existing = graph.get_edge_data(edge["from"], edge["to"]) + if existing is None or cost < existing["cost"]: + graph.add_edge(edge["from"], edge["to"], cost=cost, similarity=similarity) + + try: + path = nx.dijkstra_path(graph, start, end, weight="cost") + except (nx.NetworkXNoPath, nx.NodeNotFound): + return None, None, None + + weights = [graph[u][v]["similarity"] for u, v in zip(path[:-1], path[1:], strict=True)] + if not weights: + return path, [], 1.0 + mean = float(math.exp(sum(math.log(w) for w in weights) / len(weights))) + return path, weights, mean + + +def edge_weight_spread(graph_data: dict) -> dict: + """Descriptive statistics for the edge weights actually drawn. + + Cosine similarities in a high-dimensional space concentrate: they cluster in a + narrow band, so every edge looks equally strong and the picture stops carrying + information. This reports the spread so the claim can be checked rather than + assumed. + """ + values = [float(edge["value"]) for edge in graph_data.get("edges", [])] + if not values: + return {"n": 0, "mean": 0.0, "std": 0.0, "cv": 0.0, "min": 0.0, "max": 0.0} + array = np.asarray(values, dtype=np.float64) + mean = float(array.mean()) + std = float(array.std()) + return { + "n": len(values), + "mean": mean, + "std": std, + "cv": float(std / mean) if mean else 0.0, + "min": float(array.min()), + "max": float(array.max()), + } diff --git a/src/mpe_lkg/layers.py b/src/mpe_lkg/layers.py new file mode 100644 index 0000000..dfe4622 --- /dev/null +++ b/src/mpe_lkg/layers.py @@ -0,0 +1,305 @@ +"""Read embeddings from inside a model, not just from an embedding endpoint. + +Ollama and every other embedding API hand back one pooled vector from the top of +the stack. This module taps a chosen point *inside* a transformer instead, so the +same application can ask what the reasoning steps look like at layer 4, at layer +16, and at the output -- including for models that expose no embedding endpoint at +all, because a forward pass is all that is required. + +Three things here are load-bearing and are the usual sources of silently wrong +layer comparisons: + +* **Hooks, not ``output_hidden_states=True``.** The flag materialises + ``(n_layers + 1, batch, tokens, hidden)`` before you pool it, which is several + gigabytes on an 8B model at a realistic batch and context. A hook that pools + inside itself and drops the tensor never allocates that. +* **The last layer is normalised and the others are not.** In Hugging Face decoder + models the per-layer hidden states are the raw residual stream, while the final + entry has already been through the model's final norm. Comparing them without + applying that norm yourself compares vectors from two different spaces, and the + result looks plausible rather than broken. +* **Layer addresses are not ``model.model.layers``.** That path is a Llama detail. + The block stack is found structurally instead, which covers Llama, Qwen, Mistral, + Gemma, Phi, GPT-2, GPT-NeoX, Falcon, BERT, ViT and CLIP without a per-architecture + table. + +Requires the optional extras: pip install torch transformers +""" + +from __future__ import annotations + +import weakref +from typing import Any + +import numpy as np + +POOLINGS = ("last", "mean", "cls") + + +def _require_torch(): + try: + import torch + except ImportError as exc: # pragma: no cover - depends on the environment + raise ImportError( + "Reading internal layers needs PyTorch. Install it with:\n" + " pip install torch transformers" + ) from exc + return torch + + +def find_block_stack(model) -> tuple[str, Any]: + """Locate the repeated transformer blocks, structurally. + + Returns ``(name, module_list)`` for the longest ``nn.ModuleList`` whose children + are all instances of one class. That is what a transformer block stack looks + like in every architecture worth supporting, and it needs no per-model table. + """ + torch = _require_torch() + + best: tuple[str, Any] | None = None + for name, module in model.named_modules(): + if not isinstance(module, torch.nn.ModuleList) or len(module) < 2: + continue + kinds = {type(child).__name__ for child in module} + if len(kinds) != 1: + continue + if best is None or len(module) > len(best[1]): + best = (name, module) + + if best is None: + raise ValueError( + "Could not find a repeated block stack in this model. Address a module " + "explicitly instead, for example layer='encoder.layer.6'." + ) + return best + + +def find_final_norm(model): + """The normalisation applied after the last block, if the architecture has one. + + Needed to bring intermediate layers into the same space as the final one. Absent + on some architectures, in which case cross-layer comparison is still possible but + the scales differ and that is worth knowing. + """ + for path in ("model.norm", "transformer.ln_f", "model.final_layernorm", + "gpt_neox.final_layer_norm", "encoder.final_layer_norm", "norm", "ln_f"): + try: + return model.get_submodule(path) + except AttributeError: + continue + return None + + +def resolve_layer(model, address: str): + """Turn a layer address into a module. + + Accepts ``blocks.N`` and ``blocks.-N`` against the detected block stack, an + optional sub-path such as ``blocks.-1.mlp``, or any dotted module path. + """ + if not address.startswith("blocks"): + return model.get_submodule(address) + + stack_name, stack = find_block_stack(model) + parts = address.split(".") + if len(parts) < 2: + raise ValueError(f"Layer address {address!r} needs an index, e.g. 'blocks.-1'") + + try: + index = int(parts[1]) + except ValueError as exc: + raise ValueError(f"Layer address {address!r} has a non-numeric index") from exc + + if not -len(stack) <= index < len(stack): + raise IndexError( + f"Layer {index} is out of range: this model has {len(stack)} blocks " + f"({stack_name}.0 .. {stack_name}.{len(stack) - 1})" + ) + + full = f"{stack_name}.{index % len(stack)}" + if len(parts) > 2: + full = full + "." + ".".join(parts[2:]) + return model.get_submodule(full) + + +def describe_layers(model) -> dict: + """What can be addressed on this model. Useful when picking a layer.""" + stack_name, stack = find_block_stack(model) + return { + "block_stack": stack_name, + "n_blocks": len(stack), + "block_type": type(stack[0]).__name__, + "addresses": [f"blocks.{i}" for i in range(len(stack))], + "sub_modules": [name for name, _ in stack[0].named_children()], + "has_final_norm": find_final_norm(model) is not None, + } + + +def pool(hidden, attention_mask, how: str): + """Reduce ``(batch, tokens, hidden)`` to ``(batch, hidden)``. + + ``last`` is the architecturally correct choice for a decoder-only model: under a + causal mask only the final position has attended to the whole sequence. ``mean`` + often scores better on retrieval benchmarks anyway, so both are offered rather + than one being assumed. + """ + torch = _require_torch() + + if how == "cls": + return hidden[:, 0] + if how == "mean": + mask = attention_mask.unsqueeze(-1).to(hidden.dtype) + return (hidden * mask).sum(1) / mask.sum(1).clamp(min=1e-9) + if how == "last": + # Works for both padding sides. Getting this wrong pools the padding. + left_padded = bool((attention_mask[:, -1].sum() == attention_mask.shape[0]).item()) + if left_padded: + return hidden[:, -1] + index = attention_mask.sum(dim=1) - 1 + return hidden[torch.arange(hidden.size(0), device=hidden.device), index] + raise ValueError(f"Unknown pooling {how!r}, expected one of {POOLINGS}") + + +class HiddenStateEmbedding: + """An ``EmbeddingBackend`` that reads a chosen layer inside a local model. + + >>> probe = HiddenStateEmbedding("HuggingFaceTB/SmolLM2-135M", layer="blocks.8") + >>> probe.embed(["some text"]).shape + (1, 576) + """ + + def __init__( + self, + model_name: str, + *, + layer: str = "blocks.-1", + pooling: str = "last", + apply_final_norm: bool = True, + device: str | None = None, + max_length: int = 512, + batch_size: int = 8, + ) -> None: + torch = _require_torch() + from transformers import AutoModel, AutoTokenizer + + if pooling not in POOLINGS: + raise ValueError(f"Unknown pooling {pooling!r}, expected one of {POOLINGS}") + + self.model_name = model_name + self.layer = layer + self.pooling = pooling + self.max_length = max_length + self.batch_size = batch_size + + self.device = device or ("mps" if torch.backends.mps.is_available() else "cpu") + self.tokenizer = AutoTokenizer.from_pretrained(model_name) + if self.tokenizer.pad_token is None: + self.tokenizer.pad_token = self.tokenizer.eos_token + self.model = AutoModel.from_pretrained(model_name).to(self.device).eval() + + self._module = resolve_layer(self.model, layer) + self._final_norm = find_final_norm(self.model) if apply_final_norm else None + self.applies_final_norm = self._final_norm is not None + self._dim: int | None = None + + @property + def dim(self) -> int: + if self._dim is None: + self._dim = int(self.embed(["dimension probe"]).shape[1]) + return self._dim + + def describe(self) -> dict: + return { + "kind": "hidden-state", + "model": f"{self.model_name}@{self.layer}", + "dim": self.dim, + "layer": self.layer, + "pooling": self.pooling, + "final_norm_applied": self.applies_final_norm, + "device": str(self.device), + } + + def embed(self, texts) -> np.ndarray: + items = [" ".join(str(t).split()) or " " for t in texts] + if not items: + return np.zeros((0, self._dim or 0), dtype=np.float32) + + chunks = [ + self._embed_batch(items[i : i + self.batch_size]) + for i in range(0, len(items), self.batch_size) + ] + matrix = np.vstack(chunks).astype(np.float32) + self._dim = int(matrix.shape[1]) + norms = np.linalg.norm(matrix, axis=1, keepdims=True) + np.divide(matrix, norms, out=matrix, where=norms > 0) + return matrix + + def _embed_batch(self, items: list[str]) -> np.ndarray: + torch = _require_torch() + + batch = self.tokenizer( + items, return_tensors="pt", padding=True, truncation=True, max_length=self.max_length + ).to(self.device) + + captured: dict[str, Any] = {} + # A weak reference keeps the hook from holding this object alive through the + # closure, which is a real leak when probes are created per layer. + self_ref = weakref.ref(self) + + def hook(_module, _args, output): + probe = self_ref() + if probe is None: + return + hidden = output[0] if isinstance(output, tuple) else output + hidden = hidden.detach() + # Intermediate blocks emit the raw residual stream while the model's own + # last hidden state has already been normalised. Applying the final norm + # here is what puts every layer in one comparable space. + if probe._final_norm is not None: + hidden = probe._final_norm(hidden) + captured["pooled"] = pool(hidden, batch["attention_mask"], probe.pooling).float().cpu() + + handle = self._module.register_forward_hook(hook) + try: + with torch.inference_mode(): + self.model(**batch) + finally: + handle.remove() + + if "pooled" not in captured: + raise RuntimeError( + f"The hook on {self.layer!r} never fired. That module is not on this " + "model's forward path; use describe_layers() to see what is." + ) + return captured["pooled"].numpy() + + +class MultiLayerProbe: + """Read several layers in a single forward pass. + + Comparing layers is the whole point, and running the model once per layer costs + N times as much for exactly the same computation. + """ + + def __init__(self, model_name: str, layers: list[str], **kwargs) -> None: + self.model_name = model_name + self.layers = list(layers) + first = HiddenStateEmbedding(model_name, layer=self.layers[0], **kwargs) + self.probes = {self.layers[0]: first} + for layer in self.layers[1:]: + probe = HiddenStateEmbedding.__new__(HiddenStateEmbedding) + probe.__dict__.update(first.__dict__) + probe.layer = layer + probe._module = resolve_layer(first.model, layer) + probe._dim = None + self.probes[layer] = probe + + def embed(self, texts) -> dict[str, np.ndarray]: + return {layer: probe.embed(texts) for layer, probe in self.probes.items()} + + def describe(self) -> dict: + return { + "kind": "multi-layer", + "model": self.model_name, + "layers": self.layers, + "info": describe_layers(self.probes[self.layers[0]].model), + } diff --git a/src/mpe_lkg/reasoning.py b/src/mpe_lkg/reasoning.py new file mode 100644 index 0000000..5325a40 --- /dev/null +++ b/src/mpe_lkg/reasoning.py @@ -0,0 +1,235 @@ +"""The step-by-step reasoning loop. + +Yields plain dicts. The Flask layer turns them into server-sent events, and tests +consume them directly. Every path out of this generator ends in either a ``final`` +or an ``error`` event, which is the property that fixes the reported symptom of a +page where nothing ever appears. +""" + +from __future__ import annotations + +import json +import re +import time +from collections.abc import Iterator + +from .backends import STEP_SCHEMA, BackendError, ChatBackend, EmbeddingBackend +from .graph import build_graph, edge_weight_spread, serialize_graph_data, strongest_path + +MAX_STEPS = 20 +MIN_STEPS = 5 +MAX_STEP_CHARS = 700 +# How many times a single step may be re-asked before we take what we were given. +# The original code retried without bound and without incrementing the step +# counter, so a model that kept answering too long, or kept trying to finish early, +# held the loop forever while the browser sat waiting on a stream that never spoke. +MAX_RETRIES_PER_STEP = 3 + +SYSTEM_PROMPT = ( + "You are an expert AI assistant that explains your reasoning step by step. For each step, " + "provide a title that describes what you're doing in that step, along with the content. " + "Decide if you need another step or if you're ready to give the final answer. Respond in " + "JSON format with 'title', 'content', and 'next_action' (either 'continue' or " + "'final_answer') keys. USE AS MANY REASONING STEPS AS POSSIBLE. AT LEAST 3. BE AWARE OF " + "YOUR LIMITATIONS AS AN LLM AND WHAT YOU CAN AND CANNOT DO. IN YOUR REASONING, INCLUDE " + "EXPLORATION OF ALTERNATIVE ANSWERS. CONSIDER YOU MAY BE WRONG, AND IF YOU ARE WRONG IN " + "YOUR REASONING, WHERE IT WOULD BE. FULLY TEST ALL OTHER POSSIBILITIES. YOU CAN BE WRONG. " + "WHEN YOU SAY YOU ARE RE-EXAMINING, ACTUALLY RE-EXAMINE, AND USE ANOTHER APPROACH TO DO " + "SO. DO NOT JUST SAY YOU ARE RE-EXAMINING. USE AT LEAST 3 METHODS TO DERIVE THE ANSWER. " + "USE BEST PRACTICES. Keep the content of each step under " + f"{MAX_STEP_CHARS} characters." +) + + +def extract_json(text: str) -> dict: + """Best-effort parse of a step, for backends that ignore the response schema.""" + cleaned = re.sub(r"```(?:json)?\s*", "", text).strip() + try: + parsed = json.loads(cleaned) + if isinstance(parsed, dict): + return parsed + except json.JSONDecodeError: + pass + # Fall back to the last brace-delimited object in the text. + for candidate in reversed(re.findall(r"\{[^{}]*\}", cleaned)): + try: + parsed = json.loads(candidate) + if isinstance(parsed, dict): + return parsed + except json.JSONDecodeError: + continue + return {"title": "", "content": cleaned, "next_action": "continue"} + + +def _collect(chat: ChatBackend, messages: list[dict], max_tokens: int) -> str: + return "".join(chat.stream(messages, max_tokens, schema=STEP_SCHEMA)) + + +def _short_title(title: str, content: str, fallback: str) -> str: + """A label short enough to read on a node, without spending an extra LLM call. + + The original made a second model call per step purely to shorten a title. That + doubled the request count for something a slice does just as well. + """ + text = (title or content or fallback).strip() + text = " ".join(text.split()) + if len(text) <= 20: + return text + cut = text[:20] + # Prefer a word boundary if one is close to the end. + if " " in cut[10:]: + cut = cut[: cut.rindex(" ")] + return cut + + +def reason( + prompt: str, + *, + chat: ChatBackend, + embedder: EmbeddingBackend, + store=None, + max_steps: int = MAX_STEPS, + min_steps: int = MIN_STEPS, + top_k: int = 2, +) -> Iterator[dict]: + """Run the reasoning loop, yielding one event dict at a time.""" + messages = [ + {"role": "system", "content": SYSTEM_PROMPT}, + {"role": "user", "content": prompt}, + ] + + node_ids: list[str] = [] + labels: list[str] = [] + vectors: list = [] + total_thinking_time = 0.0 + final_answer: str | None = None + step_events = 0 + + def graph_payload() -> tuple[dict, dict | None]: + graph = build_graph(node_ids, labels, vectors, top_k=top_k) + serialized = serialize_graph_data(graph) + path, weights, mean = strongest_path(serialized) + path_data = ( + {"strongest_path": path, "path_weights": weights, "avg_similarity": mean} + if path is not None + else None + ) + return serialized, path_data + + try: + while len(node_ids) < max_steps: + step_number = len(node_ids) + 1 + step_json = None + truncated = False + + for attempt in range(MAX_RETRIES_PER_STEP): + started = time.time() + raw = _collect(chat, messages, 300) + total_thinking_time += time.time() - started + step_json = extract_json(raw) + content = str(step_json.get("content", "")).strip() + + if len(content) <= MAX_STEP_CHARS: + break + if attempt == MAX_RETRIES_PER_STEP - 1: + # Take what we have rather than asking again forever. + step_json["content"] = content[:MAX_STEP_CHARS].rstrip() + "..." + truncated = True + break + messages.append( + { + "role": "user", + "content": "Your last response was too long. Give the same step again, " + f"under {MAX_STEP_CHARS} characters.", + } + ) + + assert step_json is not None + content = str(step_json.get("content", "")).strip() + if not content: + content = "The model returned an empty step." + title = str(step_json.get("title", "")).strip() + next_action = str(step_json.get("next_action", "continue")).strip() + + node_id = f"Step{step_number}" + node_ids.append(node_id) + labels.append(f"Step {step_number}: {_short_title(title, content, node_id)}") + vectors.append(embedder.embed([content])[0]) + if store is not None: + store.add(content, vectors[-1], model=embedder.describe().get("model", "")) + + messages.append({"role": "assistant", "content": json.dumps(step_json)}) + wants_to_finish = next_action == "final_answer" or "boxed" in content.lower() + + if wants_to_finish and len(node_ids) >= min_steps: + # This step *is* the answer. Announcing it as a step and then again as + # the final answer would print the same text twice on the page and + # draw two nodes over identical content. + final_answer = content + break + + serialized, path_data = graph_payload() + step_events += 1 + yield { + "type": "step", + "step": step_number, + "title": title or f"Step {step_number}", + "content": content, + "truncated": truncated, + "graph": serialized, + "path_data": path_data, + } + + if wants_to_finish: + # Nudge once per remaining step, but never without having made + # progress -- the step counter has already advanced by now. + messages.append( + { + "role": "user", + "content": f"You have given {len(node_ids)} of {min_steps} steps. Look for " + "errors or alternatives in your answer, then continue your reasoning.", + } + ) + + if final_answer is not None and node_ids: + # The final answer *is* the last step. Relabel that node instead of adding + # a second one holding the same text: two nodes over identical content + # produce a similarity of exactly 1.00 between them, which is the + # duplicate pair visible in this project's own example screenshot. + labels[-1] = f"Final Answer: {_short_title('', final_answer, 'final')}" + else: + if final_answer is None: + messages.append( + {"role": "user", "content": "Please provide the final answer based on your reasoning above."} + ) + started = time.time() + raw = _collect(chat, messages, 300) + total_thinking_time += time.time() - started + final_answer = str(extract_json(raw).get("content", raw)).strip() or "No final answer." + + node_ids.append(f"Step{len(node_ids) + 1}") + labels.append(f"Final Answer: {_short_title('', final_answer, 'final')}") + vectors.append(embedder.embed([final_answer])[0]) + if store is not None: + store.add(final_answer, vectors[-1], model=embedder.describe().get("model", "")) + + serialized, path_data = graph_payload() + yield { + "type": "final", + "content": final_answer, + "graph": serialized, + "path_data": path_data, + } + yield { + "type": "done", + "total_time": total_thinking_time, + "steps": step_events, + "edge_spread": edge_weight_spread(serialized), + "embedding": embedder.describe(), + "chat": chat.describe(), + } + + except BackendError as exc: + yield {"type": "error", "message": str(exc), "hint": exc.hint} + except Exception as exc: # noqa: BLE001 - the stream must always say what happened + yield {"type": "error", "message": f"{type(exc).__name__}: {exc}", "hint": ""} diff --git a/src/mpe_lkg/static/vendor/marked.umd.js b/src/mpe_lkg/static/vendor/marked.umd.js new file mode 100644 index 0000000..97dfe60 --- /dev/null +++ b/src/mpe_lkg/static/vendor/marked.umd.js @@ -0,0 +1,80 @@ +/** + * marked v18.0.9 - a markdown parser + * Copyright (c) 2018-2026, MarkedJS. (MIT License) + * Copyright (c) 2011-2018, Christopher Jeffrey. (MIT License) + * https://github.com/markedjs/marked + */ + +/** + * DO NOT EDIT THIS FILE + * The code in this file is generated from files in ./src/ + */ +(function(g,f){if(typeof exports=="object"&&typeof module<"u"){module.exports=f()}else if("function"==typeof define && define.amd){define("marked",f)}else {g["marked"]=f()}}(typeof globalThis < "u" ? globalThis : typeof self < "u" ? self : this,function(){var exports={};var __exports=exports;var module={exports}; +"use strict";var j=Object.defineProperty;var we=Object.getOwnPropertyDescriptor;var ye=Object.getOwnPropertyNames;var Pe=Object.prototype.hasOwnProperty;var Se=(l,e)=>{for(var t in e)j(l,t,{get:e[t],enumerable:!0})},_e=(l,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of ye(e))!Pe.call(l,s)&&s!==t&&j(l,s,{get:()=>e[s],enumerable:!(n=we(e,s))||n.enumerable});return l};var $e=l=>_e(j({},"__esModule",{value:!0}),l);var Lt={};Se(Lt,{Hooks:()=>P,Lexer:()=>x,Marked:()=>D,Parser:()=>b,Renderer:()=>y,TextRenderer:()=>_,Tokenizer:()=>w,defaults:()=>R,getDefaults:()=>z,lexer:()=>$t,marked:()=>g,options:()=>Ot,parse:()=>St,parseInline:()=>Pt,parser:()=>_t,setOptions:()=>wt,use:()=>Re,walkTokens:()=>yt});module.exports=$e(Lt);function z(){return{async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null}}var R=z();function F(l){R=l}var E={exec:()=>null};function A(l){let e=[];return t=>{let n=Math.max(0,Math.min(3,t-1)),s=e[n];return s||(s=l(n),e[n]=s),s}}function k(l,e=""){let t=typeof l=="string"?l:l.source,n={replace:(s,r)=>{let i=typeof r=="string"?r:r.source;return i=i.replace(m.caret,"$1"),t=t.replace(s,i),n},getRegex:()=>new RegExp(t,e)};return n}var Le=((l="")=>{try{return!!new RegExp("(?<=1)(?/,blockquoteSetextReplace:/\n {0,3}((?:=+|-+) *)(?=\n|$)/g,blockquoteSetextReplace2:/^ {0,3}>[ \t]?/gm,listReplaceNesting:/^ {1,4}(?=( {4})*[^ ])/g,listIsTask:/^\[[ xX]\] +\S/,listReplaceTask:/^\[[ xX]\] +/,listTaskCheckbox:/\[[ xX]\]/,anyLine:/\n.*\n/,hrefBrackets:/^<(.*)>$/,tableDelimiter:/[:|]/,tableAlignChars:/^\||\| *$/g,tableRowBlankLine:/\n[ \t]*$/,tableAlignRight:/^ *-+: *$/,tableAlignCenter:/^ *:-+: *$/,tableAlignLeft:/^ *:-+ *$/,startATag:/^/i,startPreScriptTag:/^<(pre|code|kbd|script)(\s|>)/i,endPreScriptTag:/^<\/(pre|code|kbd|script)(\s|>)/i,startAngleBracket:/^$/,pedanticHrefTitle:/^([^'"]*[^\s])\s+(['"])(.*)\2/,unicodeAlphaNumeric:/[\p{L}\p{N}]/u,escapeTest:/[&<>"']/,escapeReplace:/[&<>"']/g,escapeTestNoEncode:/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/,escapeReplaceNoEncode:/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/g,caret:/(^|[^\[])\^/g,percentDecode:/%25/g,findPipe:/\|/g,splitPipe:/ \|/,slashPipe:/\\\|/g,carriageReturn:/\r\n|\r/g,spaceLine:/^ +$/gm,notSpaceStart:/^\S*/,endingNewline:/\n$/,listItemRegex:l=>new RegExp(`^( {0,3}${l})((?:[ ][^\\n]*)?(?:\\n|$))`),nextBulletRegex:A(l=>new RegExp(`^ {0,${l}}(?:[*+-]|\\d{1,9}[.)])((?:[ ][^\\n]*)?(?:\\n|$))`)),hrRegex:A(l=>new RegExp(`^ {0,${l}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`)),fencesBeginRegex:A(l=>new RegExp(`^ {0,${l}}(?:\`\`\`|~~~)`)),headingBeginRegex:A(l=>new RegExp(`^ {0,${l}}#`)),htmlBeginRegex:A(l=>new RegExp(`^ {0,${l}}<(?:[a-z].*>|!--)`,"i")),blockquoteBeginRegex:A(l=>new RegExp(`^ {0,${l}}>`))},Me=/^(?:[ \t]*(?:\n|$))+/,ze=/^((?: {4}| {0,3}\t)[^\n]+(?:\n(?:[ \t]*(?:\n|$))*)?)+/,Ee=/^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})([^\n]*)(?:\n|$)(?:|([\s\S]*?)(?:\n|$))(?: {0,3}\1[~`]* *(?=\n|$)|$)/,v=/^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/,Ce=/^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/,K=/ {0,3}(?:[*+-]|\d{1,9}[.)])/,ae=/^(?!bull |blockCode|fences|blockquote|heading|html|table)((?:.|\n(?!\s*?\n|bull |blockCode|fences|blockquote|heading|html|table))+?)\n {0,3}(=+|-+) *(?:\n+|$)/,le=k(ae).replace(/bull/g,K).replace(/blockCode/g,/(?: {4}| {0,3}\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}(?:\s|$)/).replace(/html/g,/ {0,3}<[^\n>]+>\n/).replace(/\|table/g,"").getRegex(),Ae=k(ae).replace(/bull/g,K).replace(/blockCode/g,/(?: {4}| {0,3}\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}(?:\s|$)/).replace(/html/g,/ {0,3}<[^\n>]+>\n/).replace(/table/g,/ {0,3}\|?(?:[:\- ]*\|)+[\:\- ]*\n/).getRegex(),W=/^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table|[ \t]+\n)[^\n]+)*)/,Ie=/^[^\n]+/,X=/(?!\s*\])(?:\\[\s\S]|[^\[\]\\])+/,Be=k(/^ {0,3}\[(label)\]: *(?:\n[ \t]*)?([^<\s][^\s]*|<.*?>)(?:(?: +(?:\n[ \t]*)?| *\n[ \t]*)(title))? *(?:\n+|$)/).replace("label",X).replace("title",/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/).getRegex(),De=k(/^(bull)([ \t][^\n]*?)?(?:\n|$)/).replace(/bull/g,K).getRegex(),Q="address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|search|section|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul",J=/|$))/,qe=k("^ {0,3}(?:<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:[^\\n]*\\n*|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?(?:\\?>[^\\n]*\\n*|$)|[^\\n]*\\n*|$)|[^\\n]*\\n*|$)|)[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$)|<(?!script|pre|style|textarea)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$)|(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$))","i").replace("comment",J).replace("tag",Q).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),pe=l=>k(W).replace("hr",v).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("|table","").replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~~~)[^\\n]*\\n").replace("list",l).replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",Q).getRegex(),ve=pe(/ {0,3}(?:[*+-]|1[.)])[ \t]+[^ \t\n]/),He=pe(/ {0,3}(?:[*+-]|\d{1,9}[.)])(?:[ \t]|\n|$)/),Ze=k(/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/).replace("paragraph",He).getRegex(),V={blockquote:Ze,code:ze,def:Be,fences:Ee,heading:Ce,hr:v,html:qe,lheading:le,list:De,newline:Me,paragraph:ve,table:E,text:Ie},ie=k("^ *([^\\n ].*)\\n {0,3}((?:\\| *)?:?-+:? *(?:\\| *:?-+:? *)*(?:\\| *)?)(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)").replace("hr",v).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("blockquote"," {0,3}>").replace("code","(?: {4}| {0,3} )[^\\n]").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~~~)[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)])[ \\t]").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",Q).getRegex(),Ge={...V,lheading:Ae,table:ie,paragraph:k(W).replace("hr",v).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("table",ie).replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~~~)[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)])[ \\t]+[^ \\t\\n]").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",Q).getRegex()},Qe={...V,html:k(`^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))`).replace("comment",J).replace(/tag/g,"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),def:/^ *\[([^\]]+)\]: *]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,heading:/^(#{1,6})(.*)(?:\n+|$)/,fences:E,lheading:/^(.+?)\n {0,3}(=+|-+) *(?:\n+|$)/,paragraph:k(W).replace("hr",v).replace("heading",` *#{1,6} *[^ +]`).replace("lheading",le).replace("|table","").replace("blockquote"," {0,3}>").replace("|fences","").replace("|list","").replace("|html","").replace("|tag","").getRegex()},Ne=/^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,je=/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,ue=/^( {2,}|\\)\n(?!\s*$)/,Fe=/^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\`+)[^`]+\k(?!`))*?\]\((?:\\[\s\S]|[^\\\(\)]|\((?:\\[\s\S]|[^\\\(\)])*\))*\)/).replace("precode-",Le?"(?`+)[^`]+\k(?!`)/).replace("html",/<(?! )[^<>]*?>/).getRegex(),he=/^(?:\*+(?:((?!\*)punct)|([^\s*]))?)|^_+(?:((?!_)punct)|([^\s_]))?/,Ve=k(he,"u").replace(/punct/g,$).getRegex(),Ye=k(he,"u").replace(/punct/g,ce).getRegex(),et=/^(?:\*+(?:((?!\*)(?!openQuote)punct)|([^\s*]))?)|^_+(?:((?!_)(?!openQuote)punct)|([^\s_]))?/,tt=k(et,"u").replace(/openQuote/g,Ke).replace(/punct/g,$).getRegex(),de="^[^_*]*?__[^_*]*?\\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\\*)punct(\\*+)(?=[\\s]|$)|notPunctSpace(\\*+)(?!\\*)(?=punctSpace|$)|(?!\\*)punctSpace(\\*+)(?=notPunctSpace)|[\\s](\\*+)(?!\\*)(?=punct)|(?!\\*)punct(\\*+)(?!\\*)(?=punct)|notPunctSpace(\\*+)(?=notPunctSpace)",nt=k(de,"gu").replace(/notPunctSpace/g,H).replace(/punctSpace/g,I).replace(/punct/g,$).getRegex(),rt=k(de,"gu").replace(/notPunctSpace/g,Xe).replace(/punctSpace/g,We).replace(/punct/g,ce).getRegex(),st="^[^_*]*?__[^_*]*?\\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\\*)punct(\\*+)(?=[\\s]|$)|notPunctSpace(\\*+)(?!\\*)(?=punctSpace|$)|(?!\\*)[\\s](\\*+)(?=notPunctSpace)|[\\s](\\*+)(?!\\*)(?=punct)|(?!\\*)punct(\\*+)(?!\\*)(?=punct)|(?:(?!\\*)punct|notPunctSpace)(\\*+)(?!\\*)(?=notPunctSpace)",it=k(st,"gu").replace(/notPunctSpace/g,H).replace(/punctSpace/g,I).replace(/punct/g,$).getRegex(),ot=k("^[^_*]*?\\*\\*[^_*]*?_[^_*]*?(?=\\*\\*)|[^_]+(?=[^_])|(?!_)punct(_+)(?=[\\s]|$)|notPunctSpace(_+)(?!_)(?=punctSpace|$)|(?!_)punctSpace(_+)(?=notPunctSpace)|[\\s](_+)(?!_)(?=punct)|(?!_)punct(_+)(?!_)(?=punct)","gu").replace(/notPunctSpace/g,H).replace(/punctSpace/g,I).replace(/punct/g,$).getRegex(),at="^[^_*]*?\\*\\*[^_*]*?_[^_*]*?(?=\\*\\*)|[^_]+(?=[^_])|(?!_)punct(_+)(?=[\\s]|$)|notPunctSpace(_+)(?!_)(?=punctSpace|$)|(?!_)[\\s](_+)(?=notPunctSpace)|[\\s](_+)(?!_)(?=punct)|(?!_)punct(_+)(?!_)(?=punct)|(?:(?!_)punct|notPunctSpace)(_+)(?!_)(?=notPunctSpace)",lt=k(at,"gu").replace(/notPunctSpace/g,H).replace(/punctSpace/g,I).replace(/punct/g,$).getRegex(),pt=k(/^~~?(?:((?!~)punct)|[^\s~])/,"u").replace(/punct/g,$).getRegex(),ut="^[^~]+(?=[^~])|(?!~)punct(~~?)(?=[\\s]|$)|notPunctSpace(~~?)(?!~)(?=punctSpace|$)|(?!~)punctSpace(~~?)(?=notPunctSpace)|[\\s](~~?)(?!~)(?=punct)|(?!~)punct(~~?)(?!~)(?=punct)|notPunctSpace(~~?)(?=notPunctSpace)",ct=k(ut,"gu").replace(/notPunctSpace/g,H).replace(/punctSpace/g,I).replace(/punct/g,$).getRegex(),ht=k(/\\(punct)/,"gu").replace(/punct/g,$).getRegex(),dt=k(/^<(scheme:[^\s\x00-\x1f<>]*|email)>/).replace("scheme",/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/).replace("email",/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/).getRegex(),kt=k(J).replace("(?:-->|$)","-->").getRegex(),gt=k("^comment|^|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^|^").replace("comment",kt).replace("attribute",/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/).getRegex(),G=/(?:\[(?:\\[\s\S]|[^\[\]\\])*\]|\\[\s\S]|`+(?!`)[^`]*?`+(?!`)|``+(?=\])|[^\[\]\\`])*?/,ft=k(/^!?\[(label)\]\(\s*(href)(?:(?:[ \t]+(?:\n[ \t]*)?|\n[ \t]*)(title))?\s*\)/).replace("label",G).replace("href",/<(?:\\.|[^\n<>\\])+>|[^ \t\n\x00-\x1f]+|(?=\))/).replace("title",/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/).getRegex(),ke=k(/^!?\[(label)\]\[(ref)\]/).replace("label",G).replace("ref",X).getRegex(),ge=k(/^!?\[(ref)\](?:\[\])?/).replace("ref",X).getRegex(),mt=k("reflink|nolink(?!\\()","g").replace("reflink",ke).replace("nolink",ge).getRegex(),oe=/[hH][tT][tT][pP][sS]?|[fF][tT][pP]/,Y={_backpedal:E,anyPunctuation:ht,autolink:dt,blockSkip:Je,br:ue,code:je,del:E,delLDelim:E,delRDelim:E,emStrongLDelim:Ve,emStrongRDelimAst:nt,emStrongRDelimUnd:ot,escape:Ne,link:ft,nolink:ge,punctuation:Ue,reflink:ke,reflinkSearch:mt,tag:gt,text:Fe,url:E},xt={...Y,emStrongLDelim:tt,emStrongRDelimAst:it,emStrongRDelimUnd:lt,link:k(/^!?\[(label)\]\((.*?)\)/).replace("label",G).getRegex(),reflink:k(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label",G).getRegex()},U={...Y,emStrongRDelimAst:rt,emStrongLDelim:Ye,delLDelim:pt,delRDelim:ct,url:k(/^((?:protocol):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/).replace("protocol",oe).replace("email",/[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/).getRegex(),_backpedal:/(?:[^?!.,:;*_'"~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_'"~)]+(?!$))+/,del:/^(~~?)(?=[^\s~])((?:\\[\s\S]|[^\\])*?(?:\\[\s\S]|[^\s~\\]))\1(?=[^~]|$)/,text:k(/^(`+|~+|[^`~])(?:(?=[`~])|(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\":">",'"':""","'":"'"},fe=l=>Rt[l];function O(l,e){if(e){if(m.escapeTest.test(l))return l.replace(m.escapeReplace,fe)}else if(m.escapeTestNoEncode.test(l))return l.replace(m.escapeReplaceNoEncode,fe);return l}function ee(l){try{l=encodeURI(l).replace(m.percentDecode,"%")}catch{return null}return l}function te(l,e){let t=l.replace(m.findPipe,(r,i,o)=>{let p=!1,a=i;for(;--a>=0&&o[a]==="\\";)p=!p;return p?"|":" |"}),n=t.split(m.splitPipe),s=0;if(n[0].trim()||n.shift(),n.length>0&&!n.at(-1)?.trim()&&n.pop(),e)if(n.length>e)n.splice(e);else for(;n.length=0&&m.blankLine.test(e[t]);)t--;return e.length-t<=2?l:e.slice(0,t+1).join(` +`)}function me(l,e){if(l.indexOf(e[1])===-1)return-1;let t=0;for(let n=0;n0?-2:-1}function xe(l,e=0){let t=e,n="";for(let s of l)if(s===" "){let r=4-t%4;n+=" ".repeat(r),t+=r}else n+=s,t++;return n}function be(l,e,t,n,s){let r=e.href,i=e.title||null,o=l[1].replace(s.other.outputLinkReplace,"$1");n.state.inLink=!0;let p={type:l[0].charAt(0)==="!"?"image":"link",raw:t,href:r,title:i,text:o,tokens:n.inlineTokens(o)};return n.state.inLink=!1,p}function Tt(l,e,t){let n=l.match(t.other.indentCodeCompensation);if(n===null)return e;let s=n[1];return e.split(` +`).map(r=>{let i=r.match(t.other.beginningSpace);if(i===null)return r;let[o]=i;return o.length>=s.length?r.slice(s.length):r}).join(` +`)}var w=class{options;rules;lexer;constructor(e){this.options=e||R}space(e){let t=this.rules.block.newline.exec(e);if(t&&t[0].length>0)return{type:"space",raw:t[0]}}code(e){let t=this.rules.block.code.exec(e);if(t){let n=this.options.pedantic?t[0]:ne(t[0]),s=n.replace(this.rules.other.codeRemoveIndent,"");return{type:"code",raw:n,codeBlockStyle:"indented",text:s}}}fences(e){let t=this.rules.block.fences.exec(e);if(t){let n=t[0],s=Tt(n,t[3]||"",this.rules);return{type:"code",raw:n,lang:t[2]?t[2].trim().replace(this.rules.inline.anyPunctuation,"$1"):t[2],text:s}}}heading(e){let t=this.rules.block.heading.exec(e);if(t){let n=t[2].trim();if(this.rules.other.endingHash.test(n)){let s=L(n,"#");(this.options.pedantic||!s||this.rules.other.endingSpaceChar.test(s))&&(n=s.trim())}return{type:"heading",raw:L(t[0],` +`),depth:t[1].length,text:n,tokens:this.lexer.inline(n)}}}hr(e){let t=this.rules.block.hr.exec(e);if(t)return{type:"hr",raw:L(t[0],` +`)}}blockquote(e){let t=this.rules.block.blockquote.exec(e);if(t){let n=L(t[0],` +`).split(` +`),s="",r="",i=[];for(;n.length>0;){let o=!1,p=[],a;for(a=0;a1,r={type:"list",raw:"",ordered:s,start:s?+n.slice(0,-1):"",loose:!1,items:[]};n=s?`\\d{1,9}\\${n.slice(-1)}`:`\\${n}`,this.options.pedantic&&(n=s?n:"[*+-]");let i=this.rules.other.listItemRegex(n),o=!1;for(;e;){let a=!1,u="",c="";if(!(t=i.exec(e))||this.rules.block.hr.test(e))break;u=t[0],e=e.substring(u.length);let h=xe(t[2].split(` +`,1)[0],t[1].length),d=e.split(` +`,1)[0],T=!h.trim(),f=0;if(this.options.pedantic?(f=2,c=h.trimStart()):T?f=t[1].length+1:(f=h.search(this.rules.other.nonSpaceChar),f=f>4?1:f,c=h.slice(f),f+=t[1].length),T&&this.rules.other.blankLine.test(d)&&(u+=d+` +`,e=e.substring(d.length+1),a=!0),!a){let S=this.rules.other.nextBulletRegex(f),M=this.rules.other.hrRegex(f),re=this.rules.other.fencesBeginRegex(f),se=this.rules.other.headingBeginRegex(f),Te=this.rules.other.htmlBeginRegex(f),Oe=this.rules.other.blockquoteBeginRegex(f);for(;e;){let N=e.split(` +`,1)[0],q;if(d=N,this.options.pedantic?(d=d.replace(this.rules.other.listReplaceNesting," "),q=d):q=d.replace(this.rules.other.tabCharGlobal," "),re.test(d)||se.test(d)||Te.test(d)||Oe.test(d)||S.test(d)||M.test(d))break;if(q.search(this.rules.other.nonSpaceChar)>=f||!d.trim())c+=` +`+q.slice(f);else{if(T||h.replace(this.rules.other.tabCharGlobal," ").search(this.rules.other.nonSpaceChar)>=4||re.test(h)||se.test(h)||M.test(h))break;c+=` +`+d}T=!d.trim(),u+=N+` +`,e=e.substring(N.length+1),h=q.slice(f)}}r.loose||(o?r.loose=!0:this.rules.other.doubleBlankLine.test(u)&&(o=!0)),r.items.push({type:"list_item",raw:u,task:!!this.options.gfm&&this.rules.other.listIsTask.test(c),loose:!1,text:c,tokens:[]}),r.raw+=u}let p=r.items.at(-1);if(p)p.raw=p.raw.trimEnd(),p.text=p.text.trimEnd();else return;r.raw=r.raw.trimEnd();for(let a of r.items){this.lexer.state.top=!1,a.tokens=this.lexer.blockTokens(a.text,[]);let u=a.tokens[0];if(a.task&&(u?.type==="text"||u?.type==="paragraph")){a.text=a.text.replace(this.rules.other.listReplaceTask,""),u.raw=u.raw.replace(this.rules.other.listReplaceTask,""),u.text=u.text.replace(this.rules.other.listReplaceTask,"");for(let h=this.lexer.inlineQueue.length-1;h>=0;h--)if(this.rules.other.listIsTask.test(this.lexer.inlineQueue[h].src)){this.lexer.inlineQueue[h].src=this.lexer.inlineQueue[h].src.replace(this.rules.other.listReplaceTask,"");break}let c=this.rules.other.listTaskCheckbox.exec(a.raw);if(c){let h={type:"checkbox",raw:c[0]+" ",checked:c[0]!=="[ ]"};a.checked=h.checked,r.loose?a.tokens[0]&&["paragraph","text"].includes(a.tokens[0].type)&&"tokens"in a.tokens[0]&&a.tokens[0].tokens?(a.tokens[0].raw=h.raw+a.tokens[0].raw,a.tokens[0].text=h.raw+a.tokens[0].text,a.tokens[0].tokens.unshift(h)):a.tokens.unshift({type:"paragraph",raw:h.raw,text:h.raw,tokens:[h]}):a.tokens.unshift(h)}}else a.task&&(a.task=!1);if(!r.loose){let c=a.tokens.filter(d=>d.type==="space"),h=c.length>0&&c.some(d=>this.rules.other.anyLine.test(d.raw));r.loose=h}}if(r.loose)for(let a of r.items){a.loose=!0;for(let u of a.tokens)u.type==="text"&&(u.type="paragraph")}return r}}html(e){let t=this.rules.block.html.exec(e);if(t){let n=ne(t[0]);return{type:"html",block:!0,raw:n,pre:t[1]==="pre"||t[1]==="script"||t[1]==="style",text:n}}}def(e){let t=this.rules.block.def.exec(e);if(t){let n=t[1].toLowerCase().replace(this.rules.other.multipleSpaceGlobal," "),s=t[2]?t[2].replace(this.rules.other.hrefBrackets,"$1").replace(this.rules.inline.anyPunctuation,"$1"):"",r=t[3]?t[3].substring(1,t[3].length-1).replace(this.rules.inline.anyPunctuation,"$1"):t[3];return{type:"def",tag:n,raw:L(t[0],` +`),href:s,title:r}}}table(e){let t=this.rules.block.table.exec(e);if(!t||!this.rules.other.tableDelimiter.test(t[2]))return;let n=te(t[1]),s=t[2].replace(this.rules.other.tableAlignChars,"").split("|"),r=t[3]?.trim()?t[3].replace(this.rules.other.tableRowBlankLine,"").split(` +`):[],i={type:"table",raw:L(t[0],` +`),header:[],align:[],rows:[]};if(n.length===s.length){for(let o of s)this.rules.other.tableAlignRight.test(o)?i.align.push("right"):this.rules.other.tableAlignCenter.test(o)?i.align.push("center"):this.rules.other.tableAlignLeft.test(o)?i.align.push("left"):i.align.push(null);for(let o=0;o({text:p,tokens:this.lexer.inline(p),header:!1,align:i.align[a]})));return i}}lheading(e){let t=this.rules.block.lheading.exec(e);if(t){let n=t[1].trim();return{type:"heading",raw:L(t[0],` +`),depth:t[2].charAt(0)==="="?1:2,text:n,tokens:this.lexer.inline(n)}}}paragraph(e){let t=this.rules.block.paragraph.exec(e);if(t){let n=t[1].charAt(t[1].length-1)===` +`?t[1].slice(0,-1):t[1];return{type:"paragraph",raw:t[0],text:n,tokens:this.lexer.inline(n)}}}text(e){let t=this.rules.block.text.exec(e);if(t)return{type:"text",raw:t[0],text:t[0],tokens:this.lexer.inline(t[0])}}escape(e){let t=this.rules.inline.escape.exec(e);if(t)return{type:"escape",raw:t[0],text:t[1]}}tag(e){let t=this.rules.inline.tag.exec(e);if(t)return!this.lexer.state.inLink&&this.rules.other.startATag.test(t[0])?this.lexer.state.inLink=!0:this.lexer.state.inLink&&this.rules.other.endATag.test(t[0])&&(this.lexer.state.inLink=!1),!this.lexer.state.inRawBlock&&this.rules.other.startPreScriptTag.test(t[0])?this.lexer.state.inRawBlock=!0:this.lexer.state.inRawBlock&&this.rules.other.endPreScriptTag.test(t[0])&&(this.lexer.state.inRawBlock=!1),{type:"html",raw:t[0],inLink:this.lexer.state.inLink,inRawBlock:this.lexer.state.inRawBlock,block:!1,text:t[0]}}link(e){let t=this.rules.inline.link.exec(e);if(t){let n=t[2].trim();if(!this.options.pedantic&&this.rules.other.startAngleBracket.test(n)){if(!this.rules.other.endAngleBracket.test(n))return;let i=L(n.slice(0,-1),"\\");if((n.length-i.length)%2===0)return}else{let i=me(t[2],"()");if(i===-2)return;if(i>-1){let p=(t[0].indexOf("!")===0?5:4)+t[1].length+i;t[2]=t[2].substring(0,i),t[0]=t[0].substring(0,p).trim(),t[3]=""}}let s=t[2],r="";if(this.options.pedantic){let i=this.rules.other.pedanticHrefTitle.exec(s);i&&(s=i[1],r=i[3])}else r=t[3]?t[3].slice(1,-1):"";return s=s.trim(),this.rules.other.startAngleBracket.test(s)&&(this.options.pedantic&&!this.rules.other.endAngleBracket.test(n)?s=s.slice(1):s=s.slice(1,-1)),be(t,{href:s&&s.replace(this.rules.inline.anyPunctuation,"$1"),title:r&&r.replace(this.rules.inline.anyPunctuation,"$1")},t[0],this.lexer,this.rules)}}reflink(e,t){let n;if((n=this.rules.inline.reflink.exec(e))||(n=this.rules.inline.nolink.exec(e))){let s=(n[2]||n[1]).replace(this.rules.other.multipleSpaceGlobal," "),r=t[s.toLowerCase()];if(!r){let i=n[0].charAt(0);return{type:"text",raw:i,text:i}}return be(n,r,n[0],this.lexer,this.rules)}}emStrong(e,t,n=""){let s=this.rules.inline.emStrongLDelim.exec(e);if(!s||!s[1]&&!s[2]&&!s[3]&&!s[4]||s[4]&&n.match(this.rules.other.unicodeAlphaNumeric))return;if(!(s[1]||s[3]||"")||!n||this.rules.inline.punctuation.exec(n)){let i=[...s[0]].length-1,o,p,a=i,u=0,c=s[0][0],h=n===c,d=c==="*"?this.rules.inline.emStrongRDelimAst:this.rules.inline.emStrongRDelimUnd;for(d.lastIndex=0,t=t.slice(-1*e.length+i);(s=d.exec(t))!==null;){if(o=s[1]||s[2]||s[3]||s[4]||s[5]||s[6],!o)continue;if(p=[...o].length,s[3]||s[4]){a+=p;continue}else if(s[5]||s[6]){if(i%3&&!((i+p)%3)){u+=p;continue}if(h)break}if(a-=p,a>0)continue;p=Math.min(p,p+a+u);let T=[...s[0]][0].length,f=e.slice(0,i+s.index+T+p);if(Math.min(i,p)%2){let M=f.slice(1,-1);return{type:"em",raw:f,text:M,tokens:this.lexer.inlineTokens(M)}}let S=f.slice(2,-2);return{type:"strong",raw:f,text:S,tokens:this.lexer.inlineTokens(S)}}}}codespan(e){let t=this.rules.inline.code.exec(e);if(t){let n=t[2].replace(this.rules.other.newLineCharGlobal," "),s=this.rules.other.nonSpaceChar.test(n),r=this.rules.other.startingSpaceChar.test(n)&&this.rules.other.endingSpaceChar.test(n);return s&&r&&(n=n.substring(1,n.length-1)),{type:"codespan",raw:t[0],text:n}}}br(e){let t=this.rules.inline.br.exec(e);if(t)return{type:"br",raw:t[0]}}del(e,t,n=""){let s=this.rules.inline.delLDelim.exec(e);if(!s)return;if(!(s[1]||"")||!n||this.rules.inline.punctuation.exec(n)){let i=[...s[0]].length-1,o,p,a=i,u=this.rules.inline.delRDelim;for(u.lastIndex=0,t=t.slice(-1*e.length+i);(s=u.exec(t))!==null;){if(o=s[1]||s[2]||s[3]||s[4]||s[5]||s[6],!o||(p=[...o].length,p!==i))continue;if(s[3]||s[4]){a+=p;continue}if(a-=p,a>0)continue;p=Math.min(p,p+a);let c=[...s[0]][0].length,h=e.slice(0,i+s.index+c+p),d=h.slice(i,-i);return{type:"del",raw:h,text:d,tokens:this.lexer.inlineTokens(d)}}}}autolink(e){let t=this.rules.inline.autolink.exec(e);if(t){let n,s;return t[2]==="@"?(n=t[1],s="mailto:"+n):(n=t[1],s=n),{type:"link",raw:t[0],text:n,href:s,tokens:[{type:"text",raw:n,text:n}]}}}url(e){let t;if(t=this.rules.inline.url.exec(e)){let n,s;if(t[2]==="@")n=t[0],s="mailto:"+n;else{let r;do r=t[0],t[0]=this.rules.inline._backpedal.exec(t[0])?.[0]??"";while(r!==t[0]);n=t[0],t[1]==="www."?s="http://"+t[0]:s=t[0]}return{type:"link",raw:t[0],text:n,href:s,tokens:[{type:"text",raw:n,text:n}]}}}inlineText(e){let t=this.rules.inline.text.exec(e);if(t){let n=this.lexer.state.inRawBlock;return{type:"text",raw:t[0],text:t[0],escaped:n}}}};var x=class l{tokens;options;state;inlineQueue;tokenizer;constructor(e){this.tokens=[],this.tokens.links=Object.create(null),this.options=e||R,this.options.tokenizer=this.options.tokenizer||new w,this.tokenizer=this.options.tokenizer,this.tokenizer.options=this.options,this.tokenizer.lexer=this,this.inlineQueue=[],this.state={inLink:!1,inRawBlock:!1,top:!0};let t={other:m,block:Z.normal,inline:B.normal};this.options.pedantic?(t.block=Z.pedantic,t.inline=B.pedantic):this.options.gfm&&(t.block=Z.gfm,this.options.breaks?t.inline=B.breaks:t.inline=B.gfm),this.tokenizer.rules=t}static get rules(){return{block:Z,inline:B}}static lex(e,t){return new l(t).lex(e)}static lexInline(e,t){return new l(t).inlineTokens(e)}lex(e){e=e.replace(m.carriageReturn,` +`),this.blockTokens(e,this.tokens);for(let t=0;t(r=o.call({lexer:this},e,t))?(e=e.substring(r.raw.length),t.push(r),!0):!1))continue;if(r=this.tokenizer.space(e)){e=e.substring(r.raw.length);let o=t.at(-1);r.raw.length===1&&o!==void 0?o.raw+=` +`:t.push(r);continue}if(r=this.tokenizer.code(e)){e=e.substring(r.raw.length);let o=t.at(-1);o?.type==="paragraph"||o?.type==="text"?(o.raw+=(o.raw.endsWith(` +`)?"":` +`)+r.raw,o.text+=` +`+r.text,this.inlineQueue.at(-1).src=o.text):t.push(r);continue}if(r=this.tokenizer.fences(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.heading(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.hr(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.blockquote(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.list(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.html(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.def(e)){e=e.substring(r.raw.length);let o=t.at(-1);o?.type==="paragraph"||o?.type==="text"?(o.raw+=(o.raw.endsWith(` +`)?"":` +`)+r.raw,o.text+=` +`+r.raw,this.inlineQueue.at(-1).src=o.text):this.tokens.links[r.tag]||(this.tokens.links[r.tag]={href:r.href,title:r.title},t.push(r));continue}if(r=this.tokenizer.table(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.lheading(e)){e=e.substring(r.raw.length),t.push(r);continue}let i=e;if(this.options.extensions?.startBlock){let o=1/0,p=e.slice(1),a;this.options.extensions.startBlock.forEach(u=>{a=u.call({lexer:this},p),typeof a=="number"&&a>=0&&(o=Math.min(o,a))}),o<1/0&&o>=0&&(i=e.substring(0,o+1))}if(this.state.top&&(r=this.tokenizer.paragraph(i))){let o=t.at(-1);n&&o?.type==="paragraph"?(o.raw+=(o.raw.endsWith(` +`)?"":` +`)+r.raw,o.text+=` +`+r.text,this.inlineQueue.pop(),this.inlineQueue.at(-1).src=o.text):t.push(r),n=i.length!==e.length,e=e.substring(r.raw.length);continue}if(r=this.tokenizer.text(e)){e=e.substring(r.raw.length);let o=t.at(-1);o?.type==="text"?(o.raw+=(o.raw.endsWith(` +`)?"":` +`)+r.raw,o.text+=` +`+r.text,this.inlineQueue.pop(),this.inlineQueue.at(-1).src=o.text):t.push(r);continue}if(e){this.infiniteLoopError(e.charCodeAt(0));break}}return this.state.top=!0,t}inline(e,t=[]){return this.inlineQueue.push({src:e,tokens:t}),t}inlineTokens(e,t=[]){this.tokenizer.lexer=this;let n=e;if(this.tokens.links){let o=Object.keys(this.tokens.links);o.length>0&&(n=n.replace(this.tokenizer.rules.inline.reflinkSearch,p=>o.includes(p.slice(p.lastIndexOf("[")+1,-1))?"["+"a".repeat(p.length-2)+"]":p))}n=n.replace(this.tokenizer.rules.inline.anyPunctuation,"++"),n=n.replace(this.tokenizer.rules.inline.blockSkip,(o,p,a)=>{let u=a?a.length:0;return o.slice(0,u)+"["+"a".repeat(o.length-u-2)+"]"}),n=this.options.hooks?.emStrongMask?.call({lexer:this},n)??n;let s=!1,r="",i=1/0;for(;e;){if(e.length(o=a.call({lexer:this},e,t))?(e=e.substring(o.raw.length),t.push(o),!0):!1))continue;if(o=this.tokenizer.escape(e)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.tag(e)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.link(e)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.reflink(e,this.tokens.links)){e=e.substring(o.raw.length);let a=t.at(-1);o.type==="text"&&a?.type==="text"?(a.raw+=o.raw,a.text+=o.text):t.push(o);continue}if(o=this.tokenizer.emStrong(e,n,r)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.codespan(e)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.br(e)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.del(e,n,r)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.autolink(e)){e=e.substring(o.raw.length),t.push(o);continue}if(!this.state.inLink&&(o=this.tokenizer.url(e))){e=e.substring(o.raw.length),t.push(o);continue}let p=e;if(this.options.extensions?.startInline){let a=1/0,u=e.slice(1),c;this.options.extensions.startInline.forEach(h=>{c=h.call({lexer:this},u),typeof c=="number"&&c>=0&&(a=Math.min(a,c))}),a<1/0&&a>=0&&(p=e.substring(0,a+1))}if(o=this.tokenizer.inlineText(p)){e=e.substring(o.raw.length),o.raw.slice(-1)!=="_"&&(r=o.raw.slice(-1)),s=!0;let a=t.at(-1);a?.type==="text"?(a.raw+=o.raw,a.text+=o.text):t.push(o);continue}if(e){this.infiniteLoopError(e.charCodeAt(0));break}}return t}infiniteLoopError(e){let t="Infinite loop on byte: "+e;if(this.options.silent)console.error(t);else throw new Error(t)}};var y=class{options;parser;constructor(e){this.options=e||R}space(e){return""}code({text:e,lang:t,escaped:n}){let s=(t||"").match(m.notSpaceStart)?.[0],r=e.replace(m.endingNewline,"")+` +`;return s?'
'+(n?r:O(r,!0))+`
+`:"
"+(n?r:O(r,!0))+`
+`}blockquote({tokens:e}){return`
+${this.parser.parse(e)}
+`}html({text:e}){return e}def(e){return""}heading({tokens:e,depth:t}){return`${this.parser.parseInline(e)} +`}hr(e){return`
+`}list(e){let t=e.ordered,n=e.start,s="";for(let o=0;o +`+s+" +`}listitem(e){return`
  • ${this.parser.parse(e.tokens)}
  • +`}checkbox({checked:e}){return" '}paragraph({tokens:e}){return`

    ${this.parser.parseInline(e)}

    +`}table(e){let t="",n="";for(let r=0;r${s}`),` + +`+t+` +`+s+`
    +`}tablerow({text:e}){return` +${e} +`}tablecell(e){let t=this.parser.parseInline(e.tokens),n=e.header?"th":"td";return(e.align?`<${n} align="${e.align}">`:`<${n}>`)+t+` +`}strong({tokens:e}){return`${this.parser.parseInline(e)}`}em({tokens:e}){return`${this.parser.parseInline(e)}`}codespan({text:e}){return`${O(e,!0)}`}br(e){return"
    "}del({tokens:e}){return`${this.parser.parseInline(e)}`}link({href:e,title:t,tokens:n}){let s=this.parser.parseInline(n),r=ee(e);if(r===null)return s;e=r;let i='
    ",i}image({href:e,title:t,text:n,tokens:s}){s&&(n=this.parser.parseInline(s,this.parser.textRenderer));let r=ee(e);if(r===null)return O(n);e=r;let i=`${O(n)}{let o=r[i].flat(1/0);n=n.concat(this.walkTokens(o,t))}):r.tokens&&(n=n.concat(this.walkTokens(r.tokens,t)))}}return n}use(...e){let t=this.defaults.extensions||{renderers:{},childTokens:{}};return e.forEach(n=>{let s={...n};if(s.async=this.defaults.async||s.async||!1,n.extensions&&(n.extensions.forEach(r=>{if(!r.name)throw new Error("extension name required");if("renderer"in r){let i=t.renderers[r.name];i?t.renderers[r.name]=function(...o){let p=r.renderer.apply(this,o);return p===!1&&(p=i.apply(this,o)),p}:t.renderers[r.name]=r.renderer}if("tokenizer"in r){if(!r.level||r.level!=="block"&&r.level!=="inline")throw new Error("extension level must be 'block' or 'inline'");let i=t[r.level];i?i.unshift(r.tokenizer):t[r.level]=[r.tokenizer],r.start&&(r.level==="block"?t.startBlock?t.startBlock.push(r.start):t.startBlock=[r.start]:r.level==="inline"&&(t.startInline?t.startInline.push(r.start):t.startInline=[r.start]))}"childTokens"in r&&r.childTokens&&(t.childTokens[r.name]=r.childTokens)}),s.extensions=t),n.renderer){let r=this.defaults.renderer||new y(this.defaults);for(let i in n.renderer){if(!(i in r))throw new Error(`renderer '${i}' does not exist`);if(["options","parser"].includes(i))continue;let o=i,p=n.renderer[o],a=r[o];r[o]=(...u)=>{let c=p.apply(r,u);return c===!1&&(c=a.apply(r,u)),c||""}}s.renderer=r}if(n.tokenizer){let r=this.defaults.tokenizer||new w(this.defaults);for(let i in n.tokenizer){if(!(i in r))throw new Error(`tokenizer '${i}' does not exist`);if(["options","rules","lexer"].includes(i))continue;let o=i,p=n.tokenizer[o],a=r[o];r[o]=(...u)=>{let c=p.apply(r,u);return c===!1&&(c=a.apply(r,u)),c}}s.tokenizer=r}if(n.hooks){let r=this.defaults.hooks||new P;for(let i in n.hooks){if(!(i in r))throw new Error(`hook '${i}' does not exist`);if(["options","block"].includes(i))continue;let o=i,p=n.hooks[o],a=r[o];P.passThroughHooks.has(i)?r[o]=u=>{if(this.defaults.async&&P.passThroughHooksRespectAsync.has(i))return(async()=>{let h=await p.call(r,u);return a.call(r,h)})();let c=p.call(r,u);return a.call(r,c)}:r[o]=(...u)=>{if(this.defaults.async)return(async()=>{let h=await p.apply(r,u);return h===!1&&(h=await a.apply(r,u)),h})();let c=p.apply(r,u);return c===!1&&(c=a.apply(r,u)),c}}s.hooks=r}if(n.walkTokens){let r=this.defaults.walkTokens,i=n.walkTokens;s.walkTokens=function(o){let p=[];return p.push(i.call(this,o)),r&&(p=p.concat(r.call(this,o))),p}}this.defaults={...this.defaults,...s}}),this}setOptions(e){return this.defaults={...this.defaults,...e},this}lexer(e,t){return x.lex(e,t??this.defaults)}parser(e,t){return b.parse(e,t??this.defaults)}parseMarkdown(e){return(n,s)=>{let r={...s},i={...this.defaults,...r},o=this.onError(!!i.silent,!!i.async);if(this.defaults.async===!0&&r.async===!1)return o(new Error("marked(): The async option was set to true by an extension. Remove async: false from the parse options object to return a Promise."));if(typeof n>"u"||n===null)return o(new Error("marked(): input parameter is undefined or null"));if(typeof n!="string")return o(new Error("marked(): input parameter is of type "+Object.prototype.toString.call(n)+", string expected"));if(i.hooks&&(i.hooks.options=i,i.hooks.block=e),i.async)return(async()=>{let p=i.hooks?await i.hooks.preprocess(n):n,u=await(i.hooks?await i.hooks.provideLexer(e):e?x.lex:x.lexInline)(p,i),c=i.hooks?await i.hooks.processAllTokens(u):u;i.walkTokens&&await Promise.all(this.walkTokens(c,i.walkTokens));let d=await(i.hooks?await i.hooks.provideParser(e):e?b.parse:b.parseInline)(c,i);return i.hooks?await i.hooks.postprocess(d):d})().catch(o);try{i.hooks&&(n=i.hooks.preprocess(n));let a=(i.hooks?i.hooks.provideLexer(e):e?x.lex:x.lexInline)(n,i);i.hooks&&(a=i.hooks.processAllTokens(a)),i.walkTokens&&this.walkTokens(a,i.walkTokens);let c=(i.hooks?i.hooks.provideParser(e):e?b.parse:b.parseInline)(a,i);return i.hooks&&(c=i.hooks.postprocess(c)),c}catch(p){return o(p)}}}onError(e,t){return n=>{if(n.message+=` +Please report this to https://github.com/markedjs/marked.`,e){let s="

    An error occurred:

    "+O(n.message+"",!0)+"
    ";return t?Promise.resolve(s):s}if(t)return Promise.reject(n);throw n}}};var C=new D;function g(l,e){return C.parse(l,e)}g.options=g.setOptions=function(l){return C.setOptions(l),g.defaults=C.defaults,F(g.defaults),g};g.getDefaults=z;g.defaults=R;function Re(...l){return C.use(...l),g.defaults=C.defaults,F(g.defaults),g}g.use=Re;g.walkTokens=function(l,e){return C.walkTokens(l,e)};g.parseInline=C.parseInline;g.Parser=b;g.parser=b.parse;g.Renderer=y;g.TextRenderer=_;g.Lexer=x;g.lexer=x.lex;g.Tokenizer=w;g.Hooks=P;g.parse=g;var Ot=g.options,wt=g.setOptions,yt=g.walkTokens,Pt=g.parseInline,St=g,_t=b.parse,$t=x.lex; + +if(__exports != exports)module.exports = exports;return module.exports})); +//# sourceMappingURL=marked.umd.js.map diff --git a/src/mpe_lkg/static/vendor/purify.min.js b/src/mpe_lkg/static/vendor/purify.min.js new file mode 100644 index 0000000..86efd85 --- /dev/null +++ b/src/mpe_lkg/static/vendor/purify.min.js @@ -0,0 +1,3 @@ +/*! @license DOMPurify 3.4.13 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.13/LICENSE */ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).DOMPurify=t()}(this,function(){"use strict";function e(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,o=Array(t);n2?n-2:0),r=2;r1?t-1:0),o=1;o1?n-1:0),r=1;r2&&void 0!==arguments[2]?arguments[2]:T;if(o&&o(e,null),!b(t))return e;let i=t.length;for(;i--;){let o=t[i];if("string"==typeof o){const e=n(o);e!==o&&(r(t)||(t[i]=e),o=e)}e[o]=!0}return e}function z(e){for(let t=0;t/g),J=c(/\${[\w\W]*/g),Q=c(/^data-[\-\w.\u00B7-\uFFFF]+$/),ee=c(/^aria-[\-\w]+$/),te=c(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i),ne=c(/^(?:\w+script|data):/i),oe=c(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g),re=c(/^html$/i),ie=c(/^[a-z][.\w]*(-[.\w]+)+$/i),ae=c(/<[/\w!]/g),le=c(/<[/\w]/g),ce=c(/<\/no(script|embed|frames)/i),se=c(/\/>/i),ue=1,fe=3,pe=7,me=8,de=9,he=11,ge=function(){return"undefined"==typeof window?null:window},ye=function(e,t,n,o){return R(e,t)&&b(e[t])?M(o.base?P(o.base):{},e[t],o.transform):n};var be=function e(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:ge();const o=t=>e(t);if(o.version="3.4.13",o.removed=[],!t||!t.document||t.document.nodeType!==de||!t.Element)return o.isSupported=!1,o;let r=t.document;const i=r,a=i.currentScript;t.DocumentFragment;const u=t.HTMLTemplateElement,f=t.Node,p=t.Element,k=t.NodeFilter,L=t.NamedNodeMap;void 0===L&&(t.NamedNodeMap||t.MozNamedAttrMap),t.HTMLFormElement;const z=t.DOMParser,be=t.trustedTypes,Te=p.prototype,Se=U(Te,"cloneNode"),Ee=U(Te,"remove"),Ae=U(Te,"nextSibling"),Ne=U(Te,"childNodes"),_e=U(Te,"parentNode"),we=U(Te,"shadowRoot"),Oe=U(Te,"attributes"),ve=f&&f.prototype?U(f.prototype,"nodeType"):null,De=f&&f.prototype?U(f.prototype,"nodeName"):null,Re=f&&f.prototype?U(f.prototype,"ownerDocument"):null;if("function"==typeof u){const e=r.createElement("template");e.content&&e.content.ownerDocument&&(r=e.content.ownerDocument)}let Ce,Ie,xe="",ke=!1,Le=0;const Me=function(){if(Le>0)throw x('A configured TRUSTED_TYPES_POLICY callback (createHTML or createScriptURL) must not call DOMPurify.sanitize, as that causes infinite recursion. Do not pass a policy whose callbacks wrap DOMPurify as TRUSTED_TYPES_POLICY; see the "DOMPurify and Trusted Types" section of the README.')},ze=function(e){Me(),Le++;try{return Ce.createHTML(e)}finally{Le--}},Pe=function(){return ke||(Ie=function(e,t){if("object"!=typeof e||"function"!=typeof e.createPolicy)return null;let n=null;const o="data-tt-policy-suffix";t&&t.hasAttribute(o)&&(n=t.getAttribute(o));const r="dompurify"+(n?"#"+n:"");try{return e.createPolicy(r,{createHTML:e=>e,createScriptURL:e=>e})}catch(e){return console.warn("TrustedTypes policy "+r+" could not be created."),null}}(be,a),ke=!0),Ie},Ue=r,Fe=Ue.implementation,He=Ue.createNodeIterator,je=Ue.createDocumentFragment,Be=Ue.getElementsByTagName,Ge=i.importNode;let We={afterSanitizeAttributes:[],afterSanitizeElements:[],afterSanitizeShadowDOM:[],beforeSanitizeAttributes:[],beforeSanitizeElements:[],beforeSanitizeShadowDOM:[],uponSanitizeAttribute:[],uponSanitizeElement:[],uponSanitizeShadowNode:[]};o.isSupported="function"==typeof n&&"function"==typeof _e&&Fe&&void 0!==Fe.createHTMLDocument;const Ye=V,qe=Z,Xe=J,$e=Q,Ke=ee,Ve=ne,Ze=oe,Je=ie;let Qe=te,et=null;const tt=M({},[...F,...H,...j,...G,...Y]);let nt=null;const ot=M({},[...q,...X,...$,...K]);let rt=Object.seal(s(null,{tagNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},allowCustomizedBuiltInElements:{writable:!0,configurable:!1,enumerable:!0,value:!1}})),it=null,at=null;const lt=Object.seal(s(null,{tagCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeCheck:{writable:!0,configurable:!1,enumerable:!0,value:null}}));let ct=!0,st=!0,ut=!1,ft=!0,pt=!1,mt=!0,dt=!1,ht=!1,gt=null,yt=null,bt=!1,Tt=!1,St=!1,Et=!1,At=!0,Nt=!1;const _t="user-content-";let wt=!0,Ot=!1,vt={},Dt=null;const Rt=M({},["annotation-xml","audio","colgroup","desc","foreignobject","head","iframe","math","mi","mn","mo","ms","mtext","noembed","noframes","noscript","plaintext","script","selectedcontent","style","svg","template","thead","title","video","xmp"]);let Ct=null;const It=M({},["audio","video","img","source","image","track"]);let xt=null;const kt=M({},["alt","class","for","id","label","name","pattern","placeholder","role","summary","title","value","style","xmlns"]),Lt="http://www.w3.org/1998/Math/MathML",Mt="http://www.w3.org/2000/svg",zt="http://www.w3.org/1999/xhtml";let Pt=zt,Ut=!1,Ft=null;const Ht=M({},[Lt,Mt,zt],S),jt=l(["mi","mo","mn","ms","mtext"]);let Bt=M({},jt);const Gt=l(["annotation-xml"]);let Wt=M({},Gt);const Yt=M({},["title","style","font","a","script"]);let qt=null;const Xt=["application/xhtml+xml","text/html"];let $t=null,Kt=null;const Vt=r.createElement("form"),Zt=function(e){return e instanceof RegExp||e instanceof Function},Jt=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};if(Kt&&Kt===e)return;e&&"object"==typeof e||(e={}),e=P(e),qt=-1===Xt.indexOf(e.PARSER_MEDIA_TYPE)?"text/html":e.PARSER_MEDIA_TYPE,$t="application/xhtml+xml"===qt?S:T,et=ye(e,"ALLOWED_TAGS",tt,{transform:$t}),nt=ye(e,"ALLOWED_ATTR",ot,{transform:$t}),Ft=ye(e,"ALLOWED_NAMESPACES",Ht,{transform:S}),xt=ye(e,"ADD_URI_SAFE_ATTR",kt,{transform:$t,base:kt}),Ct=ye(e,"ADD_DATA_URI_TAGS",It,{transform:$t,base:It}),Dt=ye(e,"FORBID_CONTENTS",Rt,{transform:$t}),it=ye(e,"FORBID_TAGS",P({}),{transform:$t}),at=ye(e,"FORBID_ATTR",P({}),{transform:$t}),vt=!!R(e,"USE_PROFILES")&&(e.USE_PROFILES&&"object"==typeof e.USE_PROFILES?P(e.USE_PROFILES):e.USE_PROFILES),ct=!1!==e.ALLOW_ARIA_ATTR,st=!1!==e.ALLOW_DATA_ATTR,ut=e.ALLOW_UNKNOWN_PROTOCOLS||!1,ft=!1!==e.ALLOW_SELF_CLOSE_IN_ATTR,pt=e.SAFE_FOR_TEMPLATES||!1,mt=!1!==e.SAFE_FOR_XML,dt=e.WHOLE_DOCUMENT||!1,Tt=e.RETURN_DOM||!1,St=e.RETURN_DOM_FRAGMENT||!1,Et=e.RETURN_TRUSTED_TYPE||!1,bt=e.FORCE_BODY||!1,At=!1!==e.SANITIZE_DOM,Nt=e.SANITIZE_NAMED_PROPS||!1,wt=!1!==e.KEEP_CONTENT,Ot=e.IN_PLACE||!1,Qe=function(e){try{return I(e,""),!0}catch(e){return!1}}(e.ALLOWED_URI_REGEXP)?e.ALLOWED_URI_REGEXP:te,Pt="string"==typeof e.NAMESPACE?e.NAMESPACE:zt,Bt=R(e,"MATHML_TEXT_INTEGRATION_POINTS")&&e.MATHML_TEXT_INTEGRATION_POINTS&&"object"==typeof e.MATHML_TEXT_INTEGRATION_POINTS?P(e.MATHML_TEXT_INTEGRATION_POINTS):M({},jt),Wt=R(e,"HTML_INTEGRATION_POINTS")&&e.HTML_INTEGRATION_POINTS&&"object"==typeof e.HTML_INTEGRATION_POINTS?P(e.HTML_INTEGRATION_POINTS):M({},Gt);const t=R(e,"CUSTOM_ELEMENT_HANDLING")&&e.CUSTOM_ELEMENT_HANDLING&&"object"==typeof e.CUSTOM_ELEMENT_HANDLING?P(e.CUSTOM_ELEMENT_HANDLING):s(null);if(rt=s(null),R(t,"tagNameCheck")&&Zt(t.tagNameCheck)&&(rt.tagNameCheck=t.tagNameCheck),R(t,"attributeNameCheck")&&Zt(t.attributeNameCheck)&&(rt.attributeNameCheck=t.attributeNameCheck),R(t,"allowCustomizedBuiltInElements")&&"boolean"==typeof t.allowCustomizedBuiltInElements&&(rt.allowCustomizedBuiltInElements=t.allowCustomizedBuiltInElements),c(rt),pt&&(st=!1),St&&(Tt=!0),vt&&(et=M({},Y),nt=s(null),!0===vt.html&&(M(et,F),M(nt,q)),!0===vt.svg&&(M(et,H),M(nt,X),M(nt,K)),!0===vt.svgFilters&&(M(et,j),M(nt,X),M(nt,K)),!0===vt.mathMl&&(M(et,G),M(nt,$),M(nt,K))),lt.tagCheck=null,lt.attributeCheck=null,R(e,"ADD_TAGS")&&("function"==typeof e.ADD_TAGS?lt.tagCheck=e.ADD_TAGS:b(e.ADD_TAGS)&&(et===tt&&(et=P(et)),M(et,e.ADD_TAGS,$t))),R(e,"ADD_ATTR")&&("function"==typeof e.ADD_ATTR?lt.attributeCheck=e.ADD_ATTR:b(e.ADD_ATTR)&&(nt===ot&&(nt=P(nt)),M(nt,e.ADD_ATTR,$t))),R(e,"ADD_URI_SAFE_ATTR")&&b(e.ADD_URI_SAFE_ATTR)&&M(xt,e.ADD_URI_SAFE_ATTR,$t),R(e,"FORBID_CONTENTS")&&b(e.FORBID_CONTENTS)&&(Dt===Rt&&(Dt=P(Dt)),M(Dt,e.FORBID_CONTENTS,$t)),R(e,"ADD_FORBID_CONTENTS")&&b(e.ADD_FORBID_CONTENTS)&&(Dt===Rt&&(Dt=P(Dt)),M(Dt,e.ADD_FORBID_CONTENTS,$t)),wt&&(et["#text"]=!0),dt&&M(et,["html","head","body"]),et.table&&(M(et,["tbody"]),delete it.tbody),e.TRUSTED_TYPES_POLICY){if("function"!=typeof e.TRUSTED_TYPES_POLICY.createHTML)throw x('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.');if("function"!=typeof e.TRUSTED_TYPES_POLICY.createScriptURL)throw x('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');const t=Ce;Ce=e.TRUSTED_TYPES_POLICY;try{xe=ze("")}catch(e){throw Ce=t,e}}else null===e.TRUSTED_TYPES_POLICY?(Ce=void 0,xe=""):(void 0===Ce&&(Ce=Pe()),Ce&&"string"==typeof xe&&(xe=ze("")));l&&l(e),Kt=e},Qt=M({},[...H,...j,...B]),en=M({},[...G,...W]),tn=function(e){let t=_e(e);t&&t.tagName||(t={namespaceURI:Pt,tagName:"template"});const n=T(e.tagName),o=T(t.tagName);return!!Ft[e.namespaceURI]&&(e.namespaceURI===Mt?function(e,t,n){return t.namespaceURI===zt?"svg"===e:t.namespaceURI===Lt?"svg"===e&&("annotation-xml"===n||Bt[n]):Boolean(Qt[e])}(n,t,o):e.namespaceURI===Lt?function(e,t,n){return t.namespaceURI===zt?"math"===e:t.namespaceURI===Mt?"math"===e&&Wt[n]:Boolean(en[e])}(n,t,o):e.namespaceURI===zt?function(e,t,n){return!(t.namespaceURI===Mt&&!Wt[n])&&!(t.namespaceURI===Lt&&!Bt[n])&&!en[e]&&(Yt[e]||!Qt[e])}(n,t,o):!("application/xhtml+xml"!==qt||!Ft[e.namespaceURI]))},nn=function(e){g(o.removed,{element:e});try{_e(e).removeChild(e)}catch(t){if(Ee(e),!_e(e))throw x("a node selected for removal could not be detached from its tree and cannot be safely returned; refusing to sanitize in place")}},on=function(e){ln(e);const t=Ne(e);if(t){const e=[];m(t,t=>{g(e,t)}),m(e,e=>{try{Ee(e)}catch(e){}})}const n=Oe(e);if(n)for(let t=n.length-1;t>=0;--t){const o=n[t],r=o&&o.name;if("string"==typeof r)try{e.removeAttribute(r)}catch(e){}}},rn=function(e,t){try{g(o.removed,{attribute:t.getAttributeNode(e),from:t})}catch(e){g(o.removed,{attribute:null,from:t})}if(t.removeAttribute(e),"is"===e)if(Tt||St)try{nn(t)}catch(e){}else try{t.setAttribute(e,"")}catch(e){}},an=function(e){const t=Oe(e);if(t)for(let n=t.length-1;n>=0;--n){const o=t[n],r=o&&o.name;if("string"==typeof r&&!nt[$t(r)])try{e.removeAttribute(r)}catch(e){}}},ln=function(e){const t=[e];for(;t.length>0;){const e=t.pop();(ve?ve(e):e.nodeType)===ue&&an(e);const n=Ne(e);if(n)for(let e=n.length-1;e>=0;--e)t.push(n[e])}},cn=function(e){let t=null,n=null;if(bt)e=""+e;else{const t=E(e,/^[\r\n\t ]+/);n=t&&t[0]}"application/xhtml+xml"===qt&&Pt===zt&&(e=''+e+"");const o=Ce?ze(e):e;if(Pt===zt)try{t=(new z).parseFromString(o,qt)}catch(e){}if(!t||!t.documentElement){t=Fe.createDocument(Pt,"template",null);try{t.documentElement.innerHTML=Ut?xe:o}catch(e){}}const i=t.body||t.documentElement;return e&&n&&i.insertBefore(r.createTextNode(n),i.childNodes[0]||null),Pt===zt?Be.call(t,dt?"html":"body")[0]:dt?t.documentElement:i},sn=function(e){const t=Re?Re(e):e.ownerDocument;return He.call(t||e,e,k.SHOW_ELEMENT|k.SHOW_COMMENT|k.SHOW_TEXT|k.SHOW_PROCESSING_INSTRUCTION|k.SHOW_CDATA_SECTION,null)},un=function(e){return e=A(e,Ye," "),e=A(e,qe," "),e=A(e,Xe," ")},fn=function(e){var t;e.normalize();const n=Re?Re(e):e.ownerDocument,o=He.call(n||e,e,k.SHOW_TEXT|k.SHOW_COMMENT|k.SHOW_CDATA_SECTION|k.SHOW_PROCESSING_INSTRUCTION,null);let r=o.nextNode();for(;r;)r.data=un(r.data),r=o.nextNode();const i=null===(t=e.querySelectorAll)||void 0===t?void 0:t.call(e,"template");i&&m(i,e=>{mn(e.content)&&fn(e.content)})},pn=function(e){const t=De?De(e):null;return"string"==typeof t&&("form"===$t(t)&&("string"!=typeof e.nodeName||"string"!=typeof e.textContent||"function"!=typeof e.removeChild||e.attributes!==Oe(e)||"function"!=typeof e.removeAttribute||"function"!=typeof e.setAttribute||"string"!=typeof e.namespaceURI||"function"!=typeof e.insertBefore||"function"!=typeof e.hasChildNodes||e.nodeType!==ve(e)||e.childNodes!==Ne(e)))},mn=function(e){if(!ve||"object"!=typeof e||null===e)return!1;try{return ve(e)===he}catch(e){return!1}},dn=function(e){if(!ve||"object"!=typeof e||null===e)return!1;try{return"number"==typeof ve(e)}catch(e){return!1}};function hn(e,t,n){0!==e.length&&m(e,e=>{e.call(o,t,n,Kt)})}const gn=function(e,t,n,o){return 0===e.length?t:t===n||t===o?P(t):t},yn=function(e,t){if(hn(We.beforeSanitizeElements,e,null),e!==t&&null===_e(e))return Ot&&ln(e),!0;if(pn(e))return nn(e),!0;const n=$t(De?De(e):e.nodeName);if(et=gn(We.uponSanitizeElement,et,tt,gt),hn(We.uponSanitizeElement,e,{tagName:n,allowedTags:et}),e!==t&&null===_e(e))return Ot&&ln(e),!0;if(function(e,t){return!!(mt&&e.hasChildNodes()&&!dn(e.firstElementChild)&&I(ae,e.textContent)&&I(ae,e.innerHTML))||!(!mt||e.namespaceURI!==zt||"style"!==t||!dn(e.firstElementChild))||e.nodeType===pe||!(!mt||e.nodeType!==me||!I(le,e.data))}(e,n))return nn(e),!0;if(it[n]||!(lt.tagCheck instanceof Function&<.tagCheck(n))&&!et[n]){const o=function(e,t,n){if(!it[t]&&Sn(t)){if(rt.tagNameCheck instanceof RegExp&&I(rt.tagNameCheck,t))return!1;if(rt.tagNameCheck instanceof Function&&rt.tagNameCheck(t))return!1}if(wt&&!Dt[t]){const t=_e(e),o=Ne(e);if(o&&t)for(let r=o.length-1;r>=0;--r){const i=e===n?Se(o[r],!0):o[r];t.insertBefore(i,Ae(e))}}return nn(e),!0}(e,n,t);return!1===o&&hn(We.afterSanitizeElements,e,null),o}if((ve?ve(e):e.nodeType)===ue&&!tn(e))return nn(e),!0;if(("noscript"===n||"noembed"===n||"noframes"===n)&&I(ce,e.innerHTML))return nn(e),!0;if(pt&&e.nodeType===fe){const t=un(e.textContent);e.textContent!==t&&(g(o.removed,{element:e.cloneNode()}),e.textContent=t)}return hn(We.afterSanitizeElements,e,null),!1},bn=function(e,t,n){if(at[t])return!1;if(mt&&"patchsrc"===t)return!1;if(mt&&"for"===t&&"label"!==e&&"output"!==e)return!1;if(At&&("id"===t||"name"===t)&&(n in r||n in Vt))return!1;const o=nt[t]||lt.attributeCheck instanceof Function&<.attributeCheck(t,e);if(st&&I($e,t));else if(ct&&I(Ke,t));else if(o)if(xt[t]);else if(I(Qe,A(n,Ze,"")));else if("src"!==t&&"xlink:href"!==t&&"href"!==t||"script"===e||0!==N(n,"data:")||!Ct[e]){if(ut&&!I(Ve,A(n,Ze,"")));else if(n)return!1}else;else if(!(Sn(e)&&(rt.tagNameCheck instanceof RegExp&&I(rt.tagNameCheck,e)||rt.tagNameCheck instanceof Function&&rt.tagNameCheck(e))&&(rt.attributeNameCheck instanceof RegExp&&I(rt.attributeNameCheck,t)||rt.attributeNameCheck instanceof Function&&rt.attributeNameCheck(t,e))||"is"===t&&rt.allowCustomizedBuiltInElements&&(rt.tagNameCheck instanceof RegExp&&I(rt.tagNameCheck,n)||rt.tagNameCheck instanceof Function&&rt.tagNameCheck(n))))return!1;return!0},Tn=M({},["annotation-xml","color-profile","font-face","font-face-format","font-face-name","font-face-src","font-face-uri","missing-glyph"]),Sn=function(e){return!Tn[T(e)]&&I(Je,e)},En=function(e,t,n,o){if(Ce&&"object"==typeof be&&"function"==typeof be.getAttributeType&&!n)switch(be.getAttributeType(e,t)){case"TrustedHTML":return ze(o);case"TrustedScriptURL":return function(e){Me(),Le++;try{return Ce.createScriptURL(e)}finally{Le--}}(o)}return o},An=function(e,t,n,r){try{n?e.setAttributeNS(n,t,r):e.setAttribute(t,r),pn(e)?nn(e):h(o.removed)}catch(n){rn(t,e)}},Nn=function(e){hn(We.beforeSanitizeAttributes,e,null);const t=e.attributes;if(!t||pn(e))return;nt=gn(We.uponSanitizeAttribute,nt,ot,yt);const n={attrName:"",attrValue:"",keepAttr:!0,allowedAttributes:nt,forceKeepAttr:void 0};let o=t.length;const r=$t(e.nodeName);for(;o--;){const i=t[o],a=i.name,l=i.namespaceURI,c=i.value,s=$t(a),u=c;let f="value"===a?u:_(u);n.attrName=s,n.attrValue=f,n.keepAttr=!0,n.forceKeepAttr=void 0,hn(We.uponSanitizeAttribute,e,n),f=n.attrValue,!Nt||"id"!==s&&"name"!==s||0===N(f,_t)||(rn(a,e),f=_t+f),mt&&I(/((--!?|])>)|<\/(style|script|title|xmp|textarea|noscript|iframe|noembed|noframes)/i,f)?rn(a,e):"attributename"===s&&E(f,"href")?rn(a,e):n.forceKeepAttr||(n.keepAttr&&(ft||!I(se,f))?(pt&&(f=un(f)),bn(r,s,f)?(f=En(r,s,l,f),f!==u&&An(e,a,l,f)):rn(a,e)):rn(a,e))}hn(We.afterSanitizeAttributes,e,null)},_n=function(e){let t=null;const n=sn(e);for(hn(We.beforeSanitizeShadowDOM,e,null);t=n.nextNode();){hn(We.uponSanitizeShadowNode,t,null),yn(t,e),Nn(t),mn(t.content)&&_n(t.content);if((ve?ve(t):t.nodeType)===ue){const e=we(t);mn(e)&&(wn(e),_n(e))}}hn(We.afterSanitizeShadowDOM,e,null)},wn=function(e){const t=[{node:e,shadow:null}];for(;t.length>0;){const e=t.pop();if(e.shadow){_n(e.shadow);continue}const n=e.node,o=(ve?ve(n):n.nodeType)===ue,r=Ne(n);if(r)for(let e=r.length-1;e>=0;--e)t.push({node:r[e],shadow:null});if(o){const e=De?De(n):null;if("string"==typeof e&&"template"===$t(e)){const e=n.content;mn(e)&&t.push({node:e,shadow:null})}}if(o){const e=we(n);mn(e)&&t.push({node:null,shadow:e},{node:e,shadow:null})}}};return o.sanitize=function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=null,r=null,a=null,l=null;if(Ut=!e,Ut&&(e="\x3c!--\x3e"),"string"!=typeof e&&!dn(e)&&"string"!=typeof(e=function(e){switch(typeof e){case"string":return e;case"number":return w(e);case"boolean":return O(e);case"bigint":return v?v(e):"0";case"symbol":return D?D(e):"Symbol()";case"undefined":default:return C(e);case"function":case"object":{if(null===e)return C(e);const t=e,n=U(t,"toString");if("function"==typeof n){const e=n(t);return"string"==typeof e?e:C(e)}return C(e)}}}(e)))throw x("dirty is not a string, aborting");if(!o.isSupported)return e;ht?(et=gt,nt=yt):Jt(t),(We.uponSanitizeElement.length>0||We.uponSanitizeAttribute.length>0)&&(et=P(et)),We.uponSanitizeAttribute.length>0&&(nt=P(nt)),o.removed=[];const c=Ot&&"string"!=typeof e&&dn(e);if(c){!function(e){if(!mt)return;const t=[e];for(;t.length>0;){const e=t.pop(),n=ve?ve(e):e.nodeType;if(n===pe||n===me&&I(le,e.data)){try{Ee(e)}catch(e){}continue}if(n===ue){const t=e,n=$t(De?De(e):e.nodeName);try{t.hasAttribute&&t.hasAttribute("patchsrc")&&t.removeAttribute("patchsrc"),t.hasAttribute&&t.hasAttribute("for")&&"label"!==n&&"output"!==n&&t.removeAttribute("for")}catch(e){}}const o=Ne(e);if(o)for(let e=o.length-1;e>=0;--e)t.push(o[e])}}(e);const t=De?De(e):e.nodeName;if("string"==typeof t){const n=$t(t);if(!et[n]||it[n])throw on(e),x("root node is forbidden and cannot be sanitized in-place")}if(pn(e))throw on(e),x("root node is clobbered and cannot be sanitized in-place");try{wn(e)}catch(t){throw on(e),t}}else if(dn(e))n=cn("\x3c!----\x3e"),r=n.ownerDocument.importNode(e,!0),r.nodeType===ue&&"BODY"===r.nodeName||"HTML"===r.nodeName?n=r:n.appendChild(r),wn(r);else{if(!Tt&&!pt&&!dt&&-1===e.indexOf("<"))return Ce&&Et?ze(e):e;if(n=cn(e),!n)return Tt?null:Et?xe:""}n&&bt&&nn(n.firstChild);const s=c?e:n;try{const e=sn(s);for(;a=e.nextNode();)yn(a,s),Nn(a),mn(a.content)&&_n(a.content)}catch(t){throw c&&(on(e),m(o.removed,e=>{e.element&&ln(e.element)})),t}if(c)return m(o.removed,e=>{e.element&&ln(e.element)}),pt&&fn(e),e;if(Tt){if(pt&&fn(n),St)for(l=je.call(n.ownerDocument);n.firstChild;)l.appendChild(n.firstChild);else l=n;return(nt.shadowroot||nt.shadowrootmode)&&(l=Ge.call(i,l,!0)),l}let u=dt?n.outerHTML:n.innerHTML;return dt&&et["!doctype"]&&n.ownerDocument&&n.ownerDocument.doctype&&n.ownerDocument.doctype.name&&I(re,n.ownerDocument.doctype.name)&&(u="\n"+u),pt&&(u=un(u)),Ce&&Et?ze(u):u},o.setConfig=function(){Jt(arguments.length>0&&void 0!==arguments[0]?arguments[0]:{}),ht=!0,gt=et,yt=nt},o.clearConfig=function(){Kt=null,ht=!1,gt=null,yt=null,Ce=Ie,xe=""},o.isValidAttribute=function(e,t,n){Kt||Jt({});const o=$t(e),r=$t(t);return bn(o,r,n)},o.addHook=function(e,t){"function"==typeof t&&R(We,e)&&g(We[e],t)},o.removeHook=function(e,t){if(R(We,e)){if(void 0!==t){const n=d(We[e],t);return-1===n?void 0:y(We[e],n,1)[0]}return h(We[e])}},o.removeHooks=function(e){R(We,e)&&(We[e]=[])},o.removeAllHooks=function(){We={afterSanitizeAttributes:[],afterSanitizeElements:[],afterSanitizeShadowDOM:[],beforeSanitizeAttributes:[],beforeSanitizeElements:[],beforeSanitizeShadowDOM:[],uponSanitizeAttribute:[],uponSanitizeElement:[],uponSanitizeShadowNode:[]}},o}();return be}); +//# sourceMappingURL=purify.min.js.map diff --git a/src/mpe_lkg/static/vendor/vis-network.min.js b/src/mpe_lkg/static/vendor/vis-network.min.js new file mode 100644 index 0000000..f63c37d --- /dev/null +++ b/src/mpe_lkg/static/vendor/vis-network.min.js @@ -0,0 +1,34 @@ +/** + * vis-network + * https://visjs.github.io/vis-network/ + * + * A dynamic, browser-based visualization library. + * + * @version 10.1.1 + * @date 2026-08-07T17:51:09.571Z + * + * @copyright (c) 2011-2017 Almende B.V, http://almende.com + * @copyright (c) 2017-2019 visjs contributors, https://github.com/visjs + * + * @license + * vis.js is dual licensed under both + * + * 1. The Apache 2.0 License + * http://www.apache.org/licenses/LICENSE-2.0 + * + * and + * + * 2. The MIT License + * http://opensource.org/licenses/MIT + * + * vis.js may be distributed under either license. + */ +!function(g,A){"object"==typeof exports&&"undefined"!=typeof module?A(exports):"function"==typeof define&&define.amd?define(["exports"],A):A((g="undefined"!=typeof globalThis?globalThis:g||self).vis=g.vis||{})}(this,function(g){function A(g,A){void 0===A&&(A={});var t=A.insertAt;if(g&&"undefined"!=typeof document){var C=document.head||document.getElementsByTagName("head")[0],I=document.createElement("style");I.type="text/css","top"===t&&C.firstChild?C.insertBefore(I,C.firstChild):C.appendChild(I),I.styleSheet?I.styleSheet.cssText=g:I.appendChild(document.createTextNode(g))}}A(".vis-overlay{bottom:0;left:0;position:absolute;right:0;top:0;z-index:10}.vis-active{box-shadow:0 0 10px #86d5f8}");A(".vis [class*=span]{min-height:0;width:auto}");A('div.vis-color-picker{background-color:#fff;border-radius:15px;box-shadow:0 0 10px 0 rgba(0,0,0,.5);display:none;height:444px;left:30px;margin-left:30px;margin-top:-140px;padding:10px;position:absolute;top:0;width:310px;z-index:1}div.vis-color-picker div.vis-arrow{left:5px;position:absolute;top:147px}div.vis-color-picker div.vis-arrow:after,div.vis-color-picker div.vis-arrow:before{border:solid transparent;content:" ";height:0;pointer-events:none;position:absolute;right:100%;top:50%;width:0}div.vis-color-picker div.vis-arrow:after{border-color:hsla(0,0%,100%,0) #fff hsla(0,0%,100%,0) hsla(0,0%,100%,0);border-width:30px;margin-top:-30px}div.vis-color-picker div.vis-color{cursor:pointer;height:289px;position:absolute;width:289px}div.vis-color-picker div.vis-brightness{position:absolute;top:313px}div.vis-color-picker div.vis-opacity{position:absolute;top:350px}div.vis-color-picker div.vis-selector{background:#4c4c4c;background:-moz-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#4c4c4c),color-stop(12%,#595959),color-stop(25%,#666),color-stop(39%,#474747),color-stop(50%,#2c2c2c),color-stop(51%,#000),color-stop(60%,#111),color-stop(76%,#2b2b2b),color-stop(91%,#1c1c1c),color-stop(100%,#131313));background:-webkit-linear-gradient(top,#4c4c4c,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313);background:-o-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:-ms-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:linear-gradient(180deg,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313);border:1px solid #fff;border-radius:15px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr="#4c4c4c",endColorstr="#131313",GradientType=0);height:15px;left:137px;position:absolute;top:137px;width:15px}div.vis-color-picker div.vis-new-color{left:159px;padding-right:2px;text-align:right}div.vis-color-picker div.vis-initial-color,div.vis-color-picker div.vis-new-color{border:1px solid rgba(0,0,0,.1);border-radius:5px;color:rgba(0,0,0,.4);font-size:10px;height:20px;line-height:20px;position:absolute;top:380px;vertical-align:middle;width:140px}div.vis-color-picker div.vis-initial-color{left:10px;padding-left:2px;text-align:left}div.vis-color-picker div.vis-label{left:10px;position:absolute;width:300px}div.vis-color-picker div.vis-label.vis-brightness{top:300px}div.vis-color-picker div.vis-label.vis-opacity{top:338px}div.vis-color-picker div.vis-button{background-color:#f7f7f7;border:2px solid #d9d9d9;border-radius:10px;cursor:pointer;height:25px;line-height:25px;position:absolute;text-align:center;top:410px;vertical-align:middle;width:68px}div.vis-color-picker div.vis-button.vis-cancel{left:5px}div.vis-color-picker div.vis-button.vis-load{left:82px}div.vis-color-picker div.vis-button.vis-apply{left:159px}div.vis-color-picker div.vis-button.vis-save{left:236px}div.vis-color-picker input.vis-range{height:20px;width:290px}');A('div.vis-configuration{display:block;float:left;font-size:12px;position:relative}div.vis-configuration-wrapper{display:block;width:700px}div.vis-configuration-wrapper:after{clear:both;content:"";display:block}div.vis-configuration.vis-config-option-container{background-color:#fff;border:2px solid #f7f8fa;border-radius:4px;display:block;left:10px;margin-top:20px;padding-left:5px;width:495px}div.vis-configuration.vis-config-button{background-color:#f7f8fa;border:2px solid #ceced0;border-radius:4px;cursor:pointer;display:block;height:25px;left:10px;line-height:25px;margin-bottom:30px;margin-top:20px;padding-left:5px;vertical-align:middle;width:495px}div.vis-configuration.vis-config-button.hover{background-color:#4588e6;border:2px solid #214373;color:#fff}div.vis-configuration.vis-config-item{display:block;float:left;height:25px;line-height:25px;vertical-align:middle;width:495px}div.vis-configuration.vis-config-item.vis-config-s2{background-color:#f7f8fa;border-radius:3px;left:10px;padding-left:5px}div.vis-configuration.vis-config-item.vis-config-s3{background-color:#e4e9f0;border-radius:3px;left:20px;padding-left:5px}div.vis-configuration.vis-config-item.vis-config-s4{background-color:#cfd8e6;border-radius:3px;left:30px;padding-left:5px}div.vis-configuration.vis-config-header{font-size:18px;font-weight:700}div.vis-configuration.vis-config-label{height:25px;line-height:25px;width:120px}div.vis-configuration.vis-config-label.vis-config-s3{width:110px}div.vis-configuration.vis-config-label.vis-config-s4{width:100px}div.vis-configuration.vis-config-colorBlock{border:1px solid #444;border-radius:2px;cursor:pointer;height:19px;margin:0;padding:0;top:1px;width:30px}input.vis-configuration.vis-config-checkbox{left:-5px}input.vis-configuration.vis-config-rangeinput{margin:0;padding:1px;pointer-events:none;position:relative;top:-5px;width:60px}input.vis-configuration.vis-config-range{-webkit-appearance:none;background-color:transparent;border:0 solid #fff;height:20px;width:300px}input.vis-configuration.vis-config-range::-webkit-slider-runnable-track{background:#dedede;background:-moz-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#dedede),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#dedede,#c8c8c8 99%);background:-o-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:linear-gradient(180deg,#dedede 0,#c8c8c8 99%);border:1px solid #999;border-radius:3px;box-shadow:0 0 3px 0 #aaa;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr="#dedede",endColorstr="#c8c8c8",GradientType=0);height:5px;width:300px}input.vis-configuration.vis-config-range::-webkit-slider-thumb{-webkit-appearance:none;background:#3876c2;background:-moz-linear-gradient(top,#3876c2 0,#385380 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#3876c2),color-stop(100%,#385380));background:-webkit-linear-gradient(top,#3876c2,#385380);background:-o-linear-gradient(top,#3876c2 0,#385380 100%);background:-ms-linear-gradient(top,#3876c2 0,#385380 100%);background:linear-gradient(180deg,#3876c2 0,#385380);border:1px solid #14334b;border-radius:50%;box-shadow:0 0 1px 0 #111927;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr="#3876c2",endColorstr="#385380",GradientType=0);height:17px;margin-top:-7px;width:17px}input.vis-configuration.vis-config-range:focus{outline:none}input.vis-configuration.vis-config-range:focus::-webkit-slider-runnable-track{background:#9d9d9d;background:-moz-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#9d9d9d),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#9d9d9d,#c8c8c8 99%);background:-o-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:linear-gradient(180deg,#9d9d9d 0,#c8c8c8 99%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr="#9d9d9d",endColorstr="#c8c8c8",GradientType=0)}input.vis-configuration.vis-config-range::-moz-range-track{background:#dedede;background:-moz-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#dedede),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#dedede,#c8c8c8 99%);background:-o-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:linear-gradient(180deg,#dedede 0,#c8c8c8 99%);border:1px solid #999;border-radius:3px;box-shadow:0 0 3px 0 #aaa;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr="#dedede",endColorstr="#c8c8c8",GradientType=0);height:10px;width:300px}input.vis-configuration.vis-config-range::-moz-range-thumb{background:#385380;border:none;border-radius:50%;height:16px;width:16px}input.vis-configuration.vis-config-range:-moz-focusring{outline:1px solid #fff;outline-offset:-1px}input.vis-configuration.vis-config-range::-ms-track{background:transparent;border-color:transparent;border-width:6px 0;color:transparent;height:5px;width:300px}input.vis-configuration.vis-config-range::-ms-fill-lower{background:#777;border-radius:10px}input.vis-configuration.vis-config-range::-ms-fill-upper{background:#ddd;border-radius:10px}input.vis-configuration.vis-config-range::-ms-thumb{background:#385380;border:none;border-radius:50%;height:16px;width:16px}input.vis-configuration.vis-config-range:focus::-ms-fill-lower{background:#888}input.vis-configuration.vis-config-range:focus::-ms-fill-upper{background:#ccc}.vis-configuration-popup{background:rgba(57,76,89,.85);border:2px solid #f2faff;border-radius:4px;color:#fff;font-size:14px;height:30px;line-height:30px;position:absolute;text-align:center;-webkit-transition:opacity .3s ease-in-out;-moz-transition:opacity .3s ease-in-out;transition:opacity .3s ease-in-out;width:150px}.vis-configuration-popup:after,.vis-configuration-popup:before{border:solid transparent;content:" ";height:0;left:100%;pointer-events:none;position:absolute;top:50%;width:0}.vis-configuration-popup:after{border-color:rgba(136,183,213,0) rgba(136,183,213,0) rgba(136,183,213,0) rgba(57,76,89,.85);border-width:8px;margin-top:-8px}.vis-configuration-popup:before{border-color:rgba(194,225,245,0) rgba(194,225,245,0) rgba(194,225,245,0) #f2faff;border-width:12px;margin-top:-12px}');A("div.vis-tooltip{background-color:#f5f4ed;border:1px solid #808074;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;box-shadow:3px 3px 10px rgba(0,0,0,.2);color:#000;font-family:verdana;font-size:14px;padding:5px;pointer-events:none;position:absolute;visibility:hidden;white-space:nowrap;z-index:5}");A('div.vis-network div.vis-navigation div.vis-button{-webkit-touch-callout:none;background-position:2px 2px;background-repeat:no-repeat;-moz-border-radius:17px;border-radius:17px;cursor:pointer;display:inline-block;height:34px;position:absolute;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;width:34px}div.vis-network div.vis-navigation div.vis-button:hover{box-shadow:0 0 3px 3px rgba(56,207,21,.3)}div.vis-network div.vis-navigation div.vis-button:active{box-shadow:0 0 1px 3px rgba(56,207,21,.95)}div.vis-network div.vis-navigation div.vis-button.vis-up{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAABphJREFUeNqcV2twU9cR/nbPlVTHxpKRbNnBLyEbPyJisLEcPwgwUMKQtjNJAzNJZkgNNJOmJaZAaDKlxaXDTIBAcJtOOzSYKSkdiimhAdIMjyT4bYgBYxA2BgcUQPLrCiGDR4qt2x+yXTASFt1/957d7zt3z3d39xDCMQWUfgAz/RI/T4pSTAJpAGL8rECAXX7QFQGq9wOHOxYO1oCgjAdJj1wtB095Giv9TFuZAIWHAziATMPhTAwiHgUkYPXFJu92lMP/2MTpB1AKUCVEgNAcleUo1M+2F8TO6crSTncb1QleAOj2OTSX3Ge1p+Va42m5JrnzbnsCE8Ov+EHgpa0LPLvCJjZ/whuIlN8wAcXG+e1LUn9hm238QU84p1Ld83nsXvuO7Lq+LzKYGAT6/dn58m/HJTYf4O3EShkT8Irpzab1Uz9sGevT5+tWn+j6NB4A5hp/5NSr43xjfd5rW5tT9e3OAhCBiCua5/WsDEls/hdvYklZSwDefmrT8eXmtzuDkb5YZ33p9ndylICAVjWxf39xw/5g5Luv/9H84ZWNcwNEypZT87rXjqyJB85UYDMJYN3U7UdLJ6/6JlgqV517teRqf9uTlug8e1zEk27HgD22o98WsTBh8fWxvjm6ApdONbGvse8LM5NUPOm1Cfabuz3nACAgxX0QEFTJAnjNvLJ+Sepb14KRHnN+Ev+1XJOhZs3Qu1mbG97J2NQgsXroa1dtxrGuf8cHi1mUtPTay0lv1DMJSCRVLtoX+FgGgDQNysBAcez89l9nbbsQSji7rlXkEhjPxb/QatHOcFu0M9zz419oFSRhj/3PuaHiyqasv1Con9NGxHAYUsoCxAqImbYSgCWmFbZQwdsur7N0eC4m6tT6/jUZ750Zeb82c+OZGLWh/2p/W+Kfrmy0hIp/aVKpTSIJEqu2QgFx2iE8CwDp0RbH7Ljng/4yXr+XT3QdyhYsodS0slGr0g2OrEUK7eCrKW82SqzCVz3/yfb6vRwM4xn9rN7JkRkOQRLmfJn2LBPxQjDBqp9lD7XbX7X8pKTP160zR2bdeiX5jYeU/nLSTztNkem3XL5eXbltRUkonBxdgZ2IIUmahUxERQSCVT+rK5hzQ89xQ6P8VaaK1f5VmRvqQ4G+lba+nlnlb5brMhvlk7FBiaPzuwQEmEQhg5BOxMjWTncHc2501cQLkjDTsMCWpyuRQxFP0xXIJfp5FyVW4Zy7KajC06ItbiIGg6ZITBxDxIgbrr1jTSM0fibGIHz8O9sKK0GAibEua9spANh4aY2VmcEg+DEkiBgR/L2hYFgGtcErkQQAMVJgBxyy9hboZzv32v+Kpr7qbEECTAIMAoaJa3qPTmNiiAAgJAjk6J5xhu6HDAIgQYGLmI29PocmMcI8MNYvT1ckfzD9H/ub5br4e4Me9WfOKqtyX6Ud2cwC449PRamifDm6Auc0rTXokci+Xo1EAgBckiDuYGLjpTvntcGIA+SFcp6uUAaAI879VhWrRteYAqn/edq758brXJ1327QMhgJcZjA3EBjNrgZjOG1PkAjyTGENMjZPq5ECQ0MDE9ERBqFZrk0OJ3i4x/7vyIjBxGERt3takgVJEAp9xq3f769WiPDNvSsJdT3HDOEASPelmoBRYT3Kzt5uMtwauJEgSOCpwrk1DIJCoNUMwj9v7MweP9XSQ8/hJPp496fZTAICvLqcyv2B7nRbrgCA03JN5h8ub7A8VqpB437xHvsOy3l3cyaB4L2uqxhti1WLMcSgZQCw7+bOooO3Pk4JBZIYYXISMV5sKH59UePM10GESRGpIf/bE92HU452HywSJIGIllctrhp6YAK5+fHds0lLtJFMXNwkV6fFqA29mROefqiMJj1h6um4a5vY/92dKGaBxIhU5zJTWW2cJmEgGOmeb3c8FxAfb9mdf2RzyGGv5MvU7QwuEySwKHFp/c/M71zA/2F7b1RajnYdLAqMukMVu2YcfmDYE2MD7H+7/Xlq6cRIJqm4zXM+qd3TGjVBir43KSLlXjiELe5TsX+3/yW/ST45PaAHbKmccWh12AP93JNZywj0kSABIobpiXRHjtZ6faout2tyZMadGLXBCxBcvl6NfaAz+tKdFmObpzWl2+tIIBACYy0t/yj34M7HvsKUK+CGassvicX7alYDwwq+vykIEqPVa+Q9gdYk5+V+UE7lj3+FGbuBM/X5JUT8QwIVSSSZiTgmoFR2MfiqYFFPfjpkyrfWPopwxP47AP1pK1g9/dqeAAAAAElFTkSuQmCC");bottom:50px;left:55px}div.vis-network div.vis-navigation div.vis-button.vis-down{background-image:url("data:image/png;base64,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");bottom:10px;left:55px}div.vis-network div.vis-navigation div.vis-button.vis-left{background-image:url("data:image/png;base64,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");bottom:10px;left:15px}div.vis-network div.vis-navigation div.vis-button.vis-right{background-image:url("data:image/png;base64,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");bottom:10px;left:95px}div.vis-network div.vis-navigation div.vis-button.vis-zoomIn{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAABiBJREFUeNqkV2tQlOcVfp7zvgvDRe66y8htXUBR1GoFI+BtFJvRtjPJBGeaH2a8DGmbttgSTWbSJEw6TWOsrbbpTIeJZGqaTipTa6LJZDTVUTYQdNAohoso6qLucnERN0Axcb/8+HaJUHDX9Pz6vnnPe57vXJ5zzkeEIwaYcwBL/VrW0TCKqZANINEvBhSk3w9eUmC9HzjcsfarOhBGKJN84GkVJHcetvqFu4SAIYELYlpm4LpQQMqoQQKVnzeO7EYV/A8NnHMAGwHWQJmAjtg895LkFa7FU1d258UvGLBGpI4AQM9dd2TrwNn4016n9bS3LqNzsD1VKPAbfhCyqflR31thAzv+La+QxotCoNi6pn1D1s9aVli/3xtOVk72fjT1XVf17E9uHZspFBD8zdk13pdCAjsOyG6KUSEEnrT/tPHluW+cw7eQ19q2z6/t2rsYJEjZ07S6d+ukwI5/yQ7RxnYC2DZnx8dbHNs6xxs85T2R9GprZcmVwYs2BYWsmBzP83m7nIVJS73jdfdd+7PjjUu/XWUCGTtPre7ZHjxTY3Kq8DoV8Ou5u49snPGrKxN58syZ9aVXBztsigoUBd+Xt2NbfZ8llaVvah+vOz9hcX+CJenWp7eOOYS6ePpTU1w39vk+AwCzFPdDQbFGFPCUY2v9hqxfXJ0shNeHLtsUFc6UequbVvdVkwLX0GXbZPpl6Zuu/ij9x/VCBU1dU7bfdFYAIDsSFRCgeOqa9hfy/nDhwfwTKOrRd0U95n0iqch9+cKS5JVtpMCdkllhAhugCHcRwAb7z1tCEp8CCXAWAJRoCFXIYnti+sYWTQ0tll0wQMk+hGUAkBOX714xbV1IyuhxHhIMC/iR5OV9M2JmuhU1Vh7PXiakrIUQhcnLXeHQxPT4GyAtFqgwgAPF5iIFWkeu1SSLCKAweXn3/ZR5rXV7SddQpy3YDoNems9qTI5hGCitm1MOAAx0aaFCerTd84zjBed3Egq9ADA/rqD7Q3ctQC4REDmkYHb8goGgsR2tz5V0DV+xUdQoqAQ81RybU4IgFWgACgpaLLCIBUo0bv63y/aXy6+WBHWz4/IHSIGAuVooiaRgWqD3AsDVoQ6bEgtOrfJUhwrf0WUtk+r8sL6wvHvk5ijVUiJSRrQZuURtfoGMuaCoRyfP/yMy0XykgAA0DPRTxNp31x2ZFuUYBgB7bK7HNdhpKz6WXq6oQCooKghMKhkgji77vBoA1jkXlAvVfRQjFMUcmxSkRWd6gpjeu32R2kxTvyhKh1DQeud8fFBh26zfOe0xuR4JgAbzywCoRSzfeDUKatJKUQK+CjKiHZ6nZ2xzBnU7B9vixTy7qCHSQEhJU3+DtdT6mAcAFiWUeP/xyPH3Jwrfo3XzysemRcEA8F5RY8h6aPE1WwMLQ4OQ/EBANHmdGWHlzZyxk3ayB0m771yGooYy+KE0l35x0iBxZehS6ie9R1PCMaDvCzWDXA4hZ283ptwcvp6qqDBnyao6AWEQrBQQ/7y+d3YoA+NBTAaElo973p8tVFCQyipW+c3pdNu7BwBOe+tm/eniK/kPFWowpMfvuKrzzw80zSKIkWsJe0bHYu163BNwMwDsv7G36ODNtzMnM5IWZfeQgscbisvLPl1aDhLTo7I8k+n/p+dw5pGeg0WKGiS31K6vvTdmA7nx9uDZ9A3xMUIpbvSezE6MSOmbNWXewHhD6dH23o7BlqQvvrwTK6KQFpXl2WyvcE6LTB2eCPSdrurvmcUnO/cVfPD6pMteyfGs3QKpUFQoS9tU/xPH8xe+Tdd693pN/pHug0Xmqntvz1uLDo9Z9v5nnrn+dvujrI1JMUJd3OY7n97ua46douOGpkdlDoUDeG7g1NS/u/5a0Og9scCsB+ysWXSoMuyFftWJvM0E31SBjmWPznHPjy+8NjdhYfeMmJl3EiNSRgCi/25fpGu4M671zjlrm685s2fEnUoQ5lrLLW8uPLj3oX9hqgxIw8n8X1LU7yMkItCHzREZrGQV6ONmy5TggHk247sL/1jFqof/hRn/AWfqC0pI+QHBIk3tICXRrFTpF8hlJaqefh6yFxQ6HwQYlK8HAKyt3WsWxl7fAAAAAElFTkSuQmCC");bottom:10px;right:15px}div.vis-network div.vis-navigation div.vis-button.vis-zoomOut{background-image:url("data:image/png;base64,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");bottom:10px;right:55px}div.vis-network div.vis-navigation div.vis-button.vis-zoomExtends{background-image:url("data:image/png;base64,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");bottom:50px;right:15px}');A('div.vis-network div.vis-manipulation{background:#fff;background:-moz-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fff),color-stop(48%,#fcfcfc),color-stop(50%,#fafafa),color-stop(100%,#fcfcfc));background:-webkit-linear-gradient(top,#fff,#fcfcfc 48%,#fafafa 50%,#fcfcfc);background:-o-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-ms-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:linear-gradient(180deg,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc);border:0 solid #d6d9d8;border-bottom:1px;box-sizing:content-box;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr="#ffffff",endColorstr="#fcfcfc",GradientType=0);height:28px;left:0;padding-top:4px;position:absolute;top:0;width:100%}div.vis-network button.vis-edit-mode,div.vis-network div.vis-edit-mode{height:30px;left:0;position:absolute;top:5px}div.vis-network button.vis-close{-webkit-touch-callout:none;background-color:transparent;background-image:url("data:image/png;base64,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/GMzLdsAADvGaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/Pgo8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjUtYzAyMSA3OS4xNTQ5MTEsIDIwMTMvMTAvMjktMTE6NDc6MTYgICAgICAgICI+CiAgIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgICAgICAgICAgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iCiAgICAgICAgICAgIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiCiAgICAgICAgICAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgICAgICAgICAgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgICAgICAgICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iPgogICAgICAgICA8eG1wOkNyZWF0b3JUb29sPkFkb2JlIFBob3Rvc2hvcCBDQyAoV2luZG93cyk8L3htcDpDcmVhdG9yVG9vbD4KICAgICAgICAgPHhtcDpDcmVhdGVEYXRlPjIwMTQtMDItMTRUMTE6NTU6MzUrMDE6MDA8L3htcDpDcmVhdGVEYXRlPgogICAgICAgICA8eG1wOk1ldGFkYXRhRGF0ZT4yMDE0LTAyLTE0VDEyOjA1OjE3KzAxOjAwPC94bXA6TWV0YWRhdGFEYXRlPgogICAgICAgICA8eG1wOk1vZGlmeURhdGU+MjAxNC0wMi0xNFQxMjowNToxNyswMTowMDwveG1wOk1vZGlmeURhdGU+CiAgICAgICAgIDx4bXBNTTpJbnN0YW5jZUlEPnhtcC5paWQ6NjU0YmM5YmQtMWI2Yi1jYjRhLTllOWQtNWY2MzgxNDVjZjk0PC94bXBNTTpJbnN0YW5jZUlEPgogICAgICAgICA8eG1wTU06RG9jdW1lbnRJRD54bXAuZGlkOjk4MmM2MGIwLWUzZjMtMDk0MC04MjU0LTFiZTliNWE0ZTE4MzwveG1wTU06RG9jdW1lbnRJRD4KICAgICAgICAgPHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD54bXAuZGlkOjk4MmM2MGIwLWUzZjMtMDk0MC04MjU0LTFiZTliNWE0ZTE4MzwveG1wTU06T3JpZ2luYWxEb2N1bWVudElEPgogICAgICAgICA8eG1wTU06SGlzdG9yeT4KICAgICAgICAgICAgPHJkZjpTZXE+CiAgICAgICAgICAgICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6YWN0aW9uPmNyZWF0ZWQ8L3N0RXZ0OmFjdGlvbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0Omluc3RhbmNlSUQ+eG1wLmlpZDo5ODJjNjBiMC1lM2YzLTA5NDAtODI1NC0xYmU5YjVhNGUxODM8L3N0RXZ0Omluc3RhbmNlSUQ+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDp3aGVuPjIwMTQtMDItMTRUMTE6NTU6MzUrMDE6MDA8L3N0RXZ0OndoZW4+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDpzb2Z0d2FyZUFnZW50PkFkb2JlIFBob3Rvc2hvcCBDQyAoV2luZG93cyk8L3N0RXZ0OnNvZnR3YXJlQWdlbnQ+CiAgICAgICAgICAgICAgIDwvcmRmOmxpPgogICAgICAgICAgICAgICA8cmRmOmxpIHJkZjpwYXJzZVR5cGU9IlJlc291cmNlIj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmFjdGlvbj5zYXZlZDwvc3RFdnQ6YWN0aW9uPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6aW5zdGFuY2VJRD54bXAuaWlkOjIxODYxNmM2LTM1MWMtNDI0OS04YWFkLWJkZDQ2ZTczNWE0NDwvc3RFdnQ6aW5zdGFuY2VJRD4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OndoZW4+MjAxNC0wMi0xNFQxMTo1NTozNSswMTowMDwvc3RFdnQ6d2hlbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OnNvZnR3YXJlQWdlbnQ+QWRvYmUgUGhvdG9zaG9wIENDIChXaW5kb3dzKTwvc3RFdnQ6c29mdHdhcmVBZ2VudD4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmNoYW5nZWQ+Lzwvc3RFdnQ6Y2hhbmdlZD4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6YWN0aW9uPnNhdmVkPC9zdEV2dDphY3Rpb24+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDppbnN0YW5jZUlEPnhtcC5paWQ6NjU0YmM5YmQtMWI2Yi1jYjRhLTllOWQtNWY2MzgxNDVjZjk0PC9zdEV2dDppbnN0YW5jZUlEPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6d2hlbj4yMDE0LTAyLTE0VDEyOjA1OjE3KzAxOjAwPC9zdEV2dDp3aGVuPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6c29mdHdhcmVBZ2VudD5BZG9iZSBQaG90b3Nob3AgQ0MgKFdpbmRvd3MpPC9zdEV2dDpzb2Z0d2FyZUFnZW50PgogICAgICAgICAgICAgICAgICA8c3RFdnQ6Y2hhbmdlZD4vPC9zdEV2dDpjaGFuZ2VkPgogICAgICAgICAgICAgICA8L3JkZjpsaT4KICAgICAgICAgICAgPC9yZGY6U2VxPgogICAgICAgICA8L3htcE1NOkhpc3Rvcnk+CiAgICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2UvcG5nPC9kYzpmb3JtYXQ+CiAgICAgICAgIDxwaG90b3Nob3A6Q29sb3JNb2RlPjM8L3Bob3Rvc2hvcDpDb2xvck1vZGU+CiAgICAgICAgIDxwaG90b3Nob3A6SUNDUHJvZmlsZT5zUkdCIElFQzYxOTY2LTIuMTwvcGhvdG9zaG9wOklDQ1Byb2ZpbGU+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgICAgIDx0aWZmOlhSZXNvbHV0aW9uPjcyMDAwMC8xMDAwMDwvdGlmZjpYUmVzb2x1dGlvbj4KICAgICAgICAgPHRpZmY6WVJlc29sdXRpb24+NzIwMDAwLzEwMDAwPC90aWZmOllSZXNvbHV0aW9uPgogICAgICAgICA8dGlmZjpSZXNvbHV0aW9uVW5pdD4yPC90aWZmOlJlc29sdXRpb25Vbml0PgogICAgICAgICA8ZXhpZjpDb2xvclNwYWNlPjE8L2V4aWY6Q29sb3JTcGFjZT4KICAgICAgICAgPGV4aWY6UGl4ZWxYRGltZW5zaW9uPjc8L2V4aWY6UGl4ZWxYRGltZW5zaW9uPgogICAgICAgICA8ZXhpZjpQaXhlbFlEaW1lbnNpb24+NzwvZXhpZjpQaXhlbFlEaW1lbnNpb24+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIAo8P3hwYWNrZXQgZW5kPSJ3Ij8+cZUZMwAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAA2ElEQVR42gDLADT/AS0tLUQFBQUVFxcXtPHx8fPl5eUNCAgITCkpKesEHx8fGgYGBjH+/v4a+Pj4qgQEBFU6OjodMTExzwQUFBSvEBAQEfX19SD19fVqNDQ0CElJSd/9/f2vAwEBAfrn5+fkBwcHLRYWFgsXFxfz29vbo9LS0uwDDQ0NDfPz81orKysXIyMj+ODg4Avh4eEa/f391gMkJCRYPz8/KUhISOMCAgKh8fHxHRsbGx4UFBQQBDk5OeY7Ozv7CAgItPb29vMEBASaJSUlTQ0NDesDAEwpT0Ko8Ri2AAAAAElFTkSuQmCC");background-position:20px 3px;background-repeat:no-repeat;border:none;cursor:pointer;height:30px;position:absolute;right:0;top:0;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;width:30px}div.vis-network button.vis-close:hover{opacity:.6}div.vis-network div.vis-edit-mode button.vis-button,div.vis-network div.vis-manipulation button.vis-button{-webkit-touch-callout:none;background-color:transparent;background-position:0 0;background-repeat:no-repeat;border:none;-moz-border-radius:15px;border-radius:15px;box-sizing:content-box;cursor:pointer;float:left;font-family:verdana;font-size:12px;height:24px;margin-left:10px;padding:0 8px;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}div.vis-network div.vis-manipulation button.vis-button:hover{box-shadow:1px 1px 8px rgba(0,0,0,.2)}div.vis-network div.vis-manipulation button.vis-button:active{box-shadow:1px 1px 8px rgba(0,0,0,.5)}div.vis-network div.vis-manipulation button.vis-button.vis-back{background-image:url("data:image/png;base64,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/GMzLdsAAEEOaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/Pgo8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjUtYzAyMSA3OS4xNTQ5MTEsIDIwMTMvMTAvMjktMTE6NDc6MTYgICAgICAgICI+CiAgIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgICAgICAgICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICAgICAgICAgICB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIKICAgICAgICAgICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgICAgICAgICAgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiCiAgICAgICAgICAgIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOmV4aWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vZXhpZi8xLjAvIj4KICAgICAgICAgPHhtcDpDcmVhdG9yVG9vbD5BZG9iZSBQaG90b3Nob3AgQ0MgKFdpbmRvd3MpPC94bXA6Q3JlYXRvclRvb2w+CiAgICAgICAgIDx4bXA6Q3JlYXRlRGF0ZT4yMDE0LTAxLTIyVDE5OjI0OjUxKzAxOjAwPC94bXA6Q3JlYXRlRGF0ZT4KICAgICAgICAgPHhtcDpNZXRhZGF0YURhdGU+MjAxNC0wMi0wNFQxNTowMTowOSswMTowMDwveG1wOk1ldGFkYXRhRGF0ZT4KICAgICAgICAgPHhtcDpNb2RpZnlEYXRlPjIwMTQtMDItMDRUMTU6MDE6MDkrMDE6MDA8L3htcDpNb2RpZnlEYXRlPgogICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3BuZzwvZGM6Zm9ybWF0PgogICAgICAgICA8eG1wTU06SW5zdGFuY2VJRD54bXAuaWlkOmI2YjQwMjVkLTAxNjQtMzU0OC1hOTdlLTQ4ZmYxMWM3NTYzMzwveG1wTU06SW5zdGFuY2VJRD4KICAgICAgICAgPHhtcE1NOkRvY3VtZW50SUQ+eG1wLmRpZDpFQTc2MkY5Njc0ODNFMzExOTQ4QkQxM0UyQkU3OTlBMTwveG1wTU06RG9jdW1lbnRJRD4KICAgICAgICAgPHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD54bXAuZGlkOjczQjYyQUFEOTE4M0UzMTE5NDhCRDEzRTJCRTc5OUExPC94bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ+CiAgICAgICAgIDx4bXBNTTpIaXN0b3J5PgogICAgICAgICAgICA8cmRmOlNlcT4KICAgICAgICAgICAgICAgPHJkZjpsaSByZGY6cGFyc2VUeXBlPSJSZXNvdXJjZSI+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDphY3Rpb24+Y3JlYXRlZDwvc3RFdnQ6YWN0aW9uPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6aW5zdGFuY2VJRD54bXAuaWlkOjczQjYyQUFEOTE4M0UzMTE5NDhCRDEzRTJCRTc5OUExPC9zdEV2dDppbnN0YW5jZUlEPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6d2hlbj4yMDE0LTAxLTIyVDE5OjI0OjUxKzAxOjAwPC9zdEV2dDp3aGVuPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6c29mdHdhcmVBZ2VudD5BZG9iZSBQaG90b3Nob3AgQ1M2IChXaW5kb3dzKTwvc3RFdnQ6c29mdHdhcmVBZ2VudD4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6YWN0aW9uPnNhdmVkPC9zdEV2dDphY3Rpb24+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDppbnN0YW5jZUlEPnhtcC5paWQ6RUE2MEEyNEUxOTg0RTMxMUFEQUZFRkU2RUMzMzNFMDM8L3N0RXZ0Omluc3RhbmNlSUQ+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDp3aGVuPjIwMTQtMDEtMjNUMTk6MTg6MDcrMDE6MDA8L3N0RXZ0OndoZW4+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDpzb2Z0d2FyZUFnZW50PkFkb2JlIFBob3Rvc2hvcCBDUzYgKFdpbmRvd3MpPC9zdEV2dDpzb2Z0d2FyZUFnZW50PgogICAgICAgICAgICAgICAgICA8c3RFdnQ6Y2hhbmdlZD4vPC9zdEV2dDpjaGFuZ2VkPgogICAgICAgICAgICAgICA8L3JkZjpsaT4KICAgICAgICAgICAgICAgPHJkZjpsaSByZGY6cGFyc2VUeXBlPSJSZXNvdXJjZSI+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDphY3Rpb24+c2F2ZWQ8L3N0RXZ0OmFjdGlvbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0Omluc3RhbmNlSUQ+eG1wLmlpZDpmOWQ3OGY4ZC1lNzY0LTc1NDgtODZiNy1iNmQ1OGMzZDg2OTc8L3N0RXZ0Omluc3RhbmNlSUQ+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDp3aGVuPjIwMTQtMDItMDRUMTU6MDE6MDkrMDE6MDA8L3N0RXZ0OndoZW4+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDpzb2Z0d2FyZUFnZW50PkFkb2JlIFBob3Rvc2hvcCBDQyAoV2luZG93cyk8L3N0RXZ0OnNvZnR3YXJlQWdlbnQ+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDpjaGFuZ2VkPi88L3N0RXZ0OmNoYW5nZWQ+CiAgICAgICAgICAgICAgIDwvcmRmOmxpPgogICAgICAgICAgICAgICA8cmRmOmxpIHJkZjpwYXJzZVR5cGU9IlJlc291cmNlIj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmFjdGlvbj5jb252ZXJ0ZWQ8L3N0RXZ0OmFjdGlvbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OnBhcmFtZXRlcnM+ZnJvbSBhcHBsaWNhdGlvbi92bmQuYWRvYmUucGhvdG9zaG9wIHRvIGltYWdlL3BuZzwvc3RFdnQ6cGFyYW1ldGVycz4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6YWN0aW9uPmRlcml2ZWQ8L3N0RXZ0OmFjdGlvbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OnBhcmFtZXRlcnM+Y29udmVydGVkIGZyb20gYXBwbGljYXRpb24vdm5kLmFkb2JlLnBob3Rvc2hvcCB0byBpbWFnZS9wbmc8L3N0RXZ0OnBhcmFtZXRlcnM+CiAgICAgICAgICAgICAgIDwvcmRmOmxpPgogICAgICAgICAgICAgICA8cmRmOmxpIHJkZjpwYXJzZVR5cGU9IlJlc291cmNlIj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmFjdGlvbj5zYXZlZDwvc3RFdnQ6YWN0aW9uPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6aW5zdGFuY2VJRD54bXAuaWlkOmI2YjQwMjVkLTAxNjQtMzU0OC1hOTdlLTQ4ZmYxMWM3NTYzMzwvc3RFdnQ6aW5zdGFuY2VJRD4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OndoZW4+MjAxNC0wMi0wNFQxNTowMTowOSswMTowMDwvc3RFdnQ6d2hlbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OnNvZnR3YXJlQWdlbnQ+QWRvYmUgUGhvdG9zaG9wIENDIChXaW5kb3dzKTwvc3RFdnQ6c29mdHdhcmVBZ2VudD4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmNoYW5nZWQ+Lzwvc3RFdnQ6Y2hhbmdlZD4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgIDwvcmRmOlNlcT4KICAgICAgICAgPC94bXBNTTpIaXN0b3J5PgogICAgICAgICA8eG1wTU06RGVyaXZlZEZyb20gcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICA8c3RSZWY6aW5zdGFuY2VJRD54bXAuaWlkOmY5ZDc4ZjhkLWU3NjQtNzU0OC04NmI3LWI2ZDU4YzNkODY5Nzwvc3RSZWY6aW5zdGFuY2VJRD4KICAgICAgICAgICAgPHN0UmVmOmRvY3VtZW50SUQ+eG1wLmRpZDpFQTc2MkY5Njc0ODNFMzExOTQ4QkQxM0UyQkU3OTlBMTwvc3RSZWY6ZG9jdW1lbnRJRD4KICAgICAgICAgICAgPHN0UmVmOm9yaWdpbmFsRG9jdW1lbnRJRD54bXAuZGlkOjczQjYyQUFEOTE4M0UzMTE5NDhCRDEzRTJCRTc5OUExPC9zdFJlZjpvcmlnaW5hbERvY3VtZW50SUQ+CiAgICAgICAgIDwveG1wTU06RGVyaXZlZEZyb20+CiAgICAgICAgIDxwaG90b3Nob3A6Q29sb3JNb2RlPjM8L3Bob3Rvc2hvcDpDb2xvck1vZGU+CiAgICAgICAgIDxwaG90b3Nob3A6SUNDUHJvZmlsZT5zUkdCIElFQzYxOTY2LTIuMTwvcGhvdG9zaG9wOklDQ1Byb2ZpbGU+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgICAgIDx0aWZmOlhSZXNvbHV0aW9uPjcyMDA5MC8xMDAwMDwvdGlmZjpYUmVzb2x1dGlvbj4KICAgICAgICAgPHRpZmY6WVJlc29sdXRpb24+NzIwMDkwLzEwMDAwPC90aWZmOllSZXNvbHV0aW9uPgogICAgICAgICA8dGlmZjpSZXNvbHV0aW9uVW5pdD4yPC90aWZmOlJlc29sdXRpb25Vbml0PgogICAgICAgICA8ZXhpZjpDb2xvclNwYWNlPjE8L2V4aWY6Q29sb3JTcGFjZT4KICAgICAgICAgPGV4aWY6UGl4ZWxYRGltZW5zaW9uPjI0PC9leGlmOlBpeGVsWERpbWVuc2lvbj4KICAgICAgICAgPGV4aWY6UGl4ZWxZRGltZW5zaW9uPjI0PC9leGlmOlBpeGVsWURpbWVuc2lvbj4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgCjw/eHBhY2tldCBlbmQ9InciPz4jq1U/AAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAVTSURBVHjanFVfTFNnFP+d77ve8qeVFbBrpcVgRrCRFikFByLxwSAaE32oRCHD6JMxxhhn8G2RxxH3MsOTbyYsmCAxPMmMMYtkIUYmK60OO0qAK23BFlNob0uh3x7WS5jLZPpLbm6+k/P9zrm5v9855PF4UFhYCABgjIExBgAgIqRSqRIi6gDQRkQ1RGTB3wgR0e8AHgH4Sa/XR/EBiAiJRAJ04cIF5Ofng4g2n0gkUkxENwF0c843LzHGQEQQQkCLExEA9ALotVgsUQAQQmgNQhJCbF5kjCEUCl0moj4t5na7fTU1NUpVVVXUYrEkASAcDhe8efOmxOfzWScmJqoBdBNR99LS0hWz2dynNSSEAF28eBGFhYVgjCEcDn9HRD1EhIMHD3o9Hs9kWVlZAh9BKBQqGB4edr58+dKZ+6JbJpOpBwBWV1fB6+rqIMsyIpHIFcZYL2MMra2tY5cuXRrfuXNnBtvAYDBk3G63oqpqZm5uzgrgSDKZjBoMhueZTAbc5XIhFouVEtFTxhiOHTs2dv78eS8+Efv374+oqpqZnZ21cs5PJJPJPlmWkyynnBuMMTQ0NHi7uro+mVyDx+Pxulwu71ZOlkqlSonoJhGhvb39s8k1nDx50ss5hyRJN9PpdKlERB2aWjSVaEilUvzBgwcORVEs5eXloXPnzk1sV8BkMiUdDofP7/dXZ7PZDilnIhw4cGBeS1pbW2P37t1zBwKBikQiUUREWFhYsHHO0d7evm0Ru90+/+rVq2rO+XGJiJxEhMrKyhgAjI6OWoeHh5tWVla+4JzDZrO9bW5unhwcHGzz+/32np4e+xaDbfoHAMxmc6ijo2O0oqIiJkkSNjY2HBIRmRljMJvNyWfPnln7+/tPMMZQXl6+0NbW9qK2tjYcj8floaEhqKpq+HCkbD3PzMwYBgYG0NXV9UuusFna2kEgELAQEQ4dOvSis7PzN41Ar9dnrl27NqCNkv/C3bt3zy4tLVmICJxzEBFJRBQmorLFxcWCqqqq0Pj4eO3Y2JhbUZTdra2tL2pra8OJRGLHnTt3zkqS9K+huHU4EhHMZnMoGo0W5OIh7nK5jjLGKq1W69vDhw8rRqMxMjc3t2t5eXnX5ORklc/nM+fl5SWnpqa+0uv1K/n5+Ws6nW5NluXNd15e3ppOp1uz2WyzZ86cGQ0Gg6ZAIFCZzWZ/lYjokRDiuN/vt7W0tMw3NTUpbrd78P79++5gMFgRiUTKHj58WMYYQ3V19etTp05tq6Lp6Wkb5xxCiEfc7XZPM8a6FxcXTfX19a/1en2Gcy5qamreNjY2/qGq6joRZe12+9Tp06e3JY/FYgWPHz8+mhvr3/CWlpbk+vp6PmOseWVlBS6XS9GSJUkSdrs93NDQ8Oe+ffvC/8fJIyMjddFo9Esi6pVleVjT2m0A8Hq9zqGhIefnjoknT544A4GAM/eDbxMReFNTE0pKSpKqqsaI6Pj8/LxVVdWM3W6PfCr5xMTE1zllXS0uLn6aSqXAGxsbodPpoNfrn6uqCs75EUVRrJFIZMfevXsXdTrdxseIE4mEPDIyUu/3++tynd8yGo29RIR0Og26fv06ioqKwBgD5xzv3r27zBjrIyJIkgSHwzFZWVmp7NmzJ1ZaWpoAgGg0WqgoSvHMzIw1GAw6tvjhitFo7NPW5fv370Hd3d0oKCgA53zTQMvLy+VCiKuSJH0rSdLmztZytIWv5RPRD0T0Y3Fx8dzWfby6ugopHo//w4mcc8iyPMc5v5FOp7/PZrOdQohWInIC2C2EgBBigYi8Qoifs9lsv06nWyIiaFxagXg8jr8GAGxuIe7LBeWhAAAAAElFTkSuQmCC")}div.vis-network div.vis-manipulation div.vis-none:hover{box-shadow:1px 1px 8px transparent;cursor:default}div.vis-network div.vis-manipulation div.vis-none:active{box-shadow:1px 1px 8px transparent}div.vis-network div.vis-manipulation div.vis-none{line-height:23px;padding:0}div.vis-network div.vis-manipulation div.notification{font-weight:700;margin:2px}div.vis-network div.vis-manipulation button.vis-button.vis-add{background-image:url("data:image/png;base64,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/GMzLdsAAEEOaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/Pgo8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjUtYzAyMSA3OS4xNTQ5MTEsIDIwMTMvMTAvMjktMTE6NDc6MTYgICAgICAgICI+CiAgIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgICAgICAgICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICAgICAgICAgICB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIKICAgICAgICAgICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgICAgICAgICAgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiCiAgICAgICAgICAgIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOmV4aWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vZXhpZi8xLjAvIj4KICAgICAgICAgPHhtcDpDcmVhdG9yVG9vbD5BZG9iZSBQaG90b3Nob3AgQ0MgKFdpbmRvd3MpPC94bXA6Q3JlYXRvclRvb2w+CiAgICAgICAgIDx4bXA6Q3JlYXRlRGF0ZT4yMDE0LTAxLTIyVDE5OjI0OjUxKzAxOjAwPC94bXA6Q3JlYXRlRGF0ZT4KICAgICAgICAgPHhtcDpNZXRhZGF0YURhdGU+MjAxNC0wMi0wNFQxNDo0MDoyOSswMTowMDwveG1wOk1ldGFkYXRhRGF0ZT4KICAgICAgICAgPHhtcDpNb2RpZnlEYXRlPjIwMTQtMDItMDRUMTQ6NDA6MjkrMDE6MDA8L3htcDpNb2RpZnlEYXRlPgogICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3BuZzwvZGM6Zm9ybWF0PgogICAgICAgICA8eG1wTU06SW5zdGFuY2VJRD54bXAuaWlkOjVkNWIwNmQwLTVmMjAtOGE0NC1hMzIwLWZmMTEzMzQwNDc0YjwveG1wTU06SW5zdGFuY2VJRD4KICAgICAgICAgPHhtcE1NOkRvY3VtZW50SUQ+eG1wLmRpZDpFQTc2MkY5Njc0ODNFMzExOTQ4QkQxM0UyQkU3OTlBMTwveG1wTU06RG9jdW1lbnRJRD4KICAgICAgICAgPHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD54bXAuZGlkOjczQjYyQUFEOTE4M0UzMTE5NDhCRDEzRTJCRTc5OUExPC94bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ+CiAgICAgICAgIDx4bXBNTTpIaXN0b3J5PgogICAgICAgICAgICA8cmRmOlNlcT4KICAgICAgICAgICAgICAgPHJkZjpsaSByZGY6cGFyc2VUeXBlPSJSZXNvdXJjZSI+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDphY3Rpb24+Y3JlYXRlZDwvc3RFdnQ6YWN0aW9uPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6aW5zdGFuY2VJRD54bXAuaWlkOjczQjYyQUFEOTE4M0UzMTE5NDhCRDEzRTJCRTc5OUExPC9zdEV2dDppbnN0YW5jZUlEPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6d2hlbj4yMDE0LTAxLTIyVDE5OjI0OjUxKzAxOjAwPC9zdEV2dDp3aGVuPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6c29mdHdhcmVBZ2VudD5BZG9iZSBQaG90b3Nob3AgQ1M2IChXaW5kb3dzKTwvc3RFdnQ6c29mdHdhcmVBZ2VudD4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6YWN0aW9uPnNhdmVkPC9zdEV2dDphY3Rpb24+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDppbnN0YW5jZUlEPnhtcC5paWQ6RUE2MEEyNEUxOTg0RTMxMUFEQUZFRkU2RUMzMzNFMDM8L3N0RXZ0Omluc3RhbmNlSUQ+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDp3aGVuPjIwMTQtMDEtMjNUMTk6MTg6MDcrMDE6MDA8L3N0RXZ0OndoZW4+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDpzb2Z0d2FyZUFnZW50PkFkb2JlIFBob3Rvc2hvcCBDUzYgKFdpbmRvd3MpPC9zdEV2dDpzb2Z0d2FyZUFnZW50PgogICAgICAgICAgICAgICAgICA8c3RFdnQ6Y2hhbmdlZD4vPC9zdEV2dDpjaGFuZ2VkPgogICAgICAgICAgICAgICA8L3JkZjpsaT4KICAgICAgICAgICAgICAgPHJkZjpsaSByZGY6cGFyc2VUeXBlPSJSZXNvdXJjZSI+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDphY3Rpb24+c2F2ZWQ8L3N0RXZ0OmFjdGlvbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0Omluc3RhbmNlSUQ+eG1wLmlpZDo2OWVmYWE1NS01ZTI5LTIzNGUtYTUzMy0xNDkxYjM1NDNmYmE8L3N0RXZ0Omluc3RhbmNlSUQ+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDp3aGVuPjIwMTQtMDItMDRUMTQ6NDA6MjkrMDE6MDA8L3N0RXZ0OndoZW4+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDpzb2Z0d2FyZUFnZW50PkFkb2JlIFBob3Rvc2hvcCBDQyAoV2luZG93cyk8L3N0RXZ0OnNvZnR3YXJlQWdlbnQ+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDpjaGFuZ2VkPi88L3N0RXZ0OmNoYW5nZWQ+CiAgICAgICAgICAgICAgIDwvcmRmOmxpPgogICAgICAgICAgICAgICA8cmRmOmxpIHJkZjpwYXJzZVR5cGU9IlJlc291cmNlIj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmFjdGlvbj5jb252ZXJ0ZWQ8L3N0RXZ0OmFjdGlvbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OnBhcmFtZXRlcnM+ZnJvbSBhcHBsaWNhdGlvbi92bmQuYWRvYmUucGhvdG9zaG9wIHRvIGltYWdlL3BuZzwvc3RFdnQ6cGFyYW1ldGVycz4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6YWN0aW9uPmRlcml2ZWQ8L3N0RXZ0OmFjdGlvbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OnBhcmFtZXRlcnM+Y29udmVydGVkIGZyb20gYXBwbGljYXRpb24vdm5kLmFkb2JlLnBob3Rvc2hvcCB0byBpbWFnZS9wbmc8L3N0RXZ0OnBhcmFtZXRlcnM+CiAgICAgICAgICAgICAgIDwvcmRmOmxpPgogICAgICAgICAgICAgICA8cmRmOmxpIHJkZjpwYXJzZVR5cGU9IlJlc291cmNlIj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmFjdGlvbj5zYXZlZDwvc3RFdnQ6YWN0aW9uPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6aW5zdGFuY2VJRD54bXAuaWlkOjVkNWIwNmQwLTVmMjAtOGE0NC1hMzIwLWZmMTEzMzQwNDc0Yjwvc3RFdnQ6aW5zdGFuY2VJRD4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OndoZW4+MjAxNC0wMi0wNFQxNDo0MDoyOSswMTowMDwvc3RFdnQ6d2hlbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OnNvZnR3YXJlQWdlbnQ+QWRvYmUgUGhvdG9zaG9wIENDIChXaW5kb3dzKTwvc3RFdnQ6c29mdHdhcmVBZ2VudD4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmNoYW5nZWQ+Lzwvc3RFdnQ6Y2hhbmdlZD4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgIDwvcmRmOlNlcT4KICAgICAgICAgPC94bXBNTTpIaXN0b3J5PgogICAgICAgICA8eG1wTU06RGVyaXZlZEZyb20gcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICA8c3RSZWY6aW5zdGFuY2VJRD54bXAuaWlkOjY5ZWZhYTU1LTVlMjktMjM0ZS1hNTMzLTE0OTFiMzU0M2ZiYTwvc3RSZWY6aW5zdGFuY2VJRD4KICAgICAgICAgICAgPHN0UmVmOmRvY3VtZW50SUQ+eG1wLmRpZDpFQTc2MkY5Njc0ODNFMzExOTQ4QkQxM0UyQkU3OTlBMTwvc3RSZWY6ZG9jdW1lbnRJRD4KICAgICAgICAgICAgPHN0UmVmOm9yaWdpbmFsRG9jdW1lbnRJRD54bXAuZGlkOjczQjYyQUFEOTE4M0UzMTE5NDhCRDEzRTJCRTc5OUExPC9zdFJlZjpvcmlnaW5hbERvY3VtZW50SUQ+CiAgICAgICAgIDwveG1wTU06RGVyaXZlZEZyb20+CiAgICAgICAgIDxwaG90b3Nob3A6Q29sb3JNb2RlPjM8L3Bob3Rvc2hvcDpDb2xvck1vZGU+CiAgICAgICAgIDxwaG90b3Nob3A6SUNDUHJvZmlsZT5zUkdCIElFQzYxOTY2LTIuMTwvcGhvdG9zaG9wOklDQ1Byb2ZpbGU+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgICAgIDx0aWZmOlhSZXNvbHV0aW9uPjcyMDA5MC8xMDAwMDwvdGlmZjpYUmVzb2x1dGlvbj4KICAgICAgICAgPHRpZmY6WVJlc29sdXRpb24+NzIwMDkwLzEwMDAwPC90aWZmOllSZXNvbHV0aW9uPgogICAgICAgICA8dGlmZjpSZXNvbHV0aW9uVW5pdD4yPC90aWZmOlJlc29sdXRpb25Vbml0PgogICAgICAgICA8ZXhpZjpDb2xvclNwYWNlPjE8L2V4aWY6Q29sb3JTcGFjZT4KICAgICAgICAgPGV4aWY6UGl4ZWxYRGltZW5zaW9uPjI0PC9leGlmOlBpeGVsWERpbWVuc2lvbj4KICAgICAgICAgPGV4aWY6UGl4ZWxZRGltZW5zaW9uPjI0PC9leGlmOlBpeGVsWURpbWVuc2lvbj4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgCjw/eHBhY2tldCBlbmQ9InciPz5WKqp9AAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAYXSURBVHjafFZtUFTXGX7e9z27sveuMCwYV8ElrA7YSFYHtJUPkaaI0aRqG8wP00zUzljDINNSA/2ROtpO24SxnahlxjYd7SSjmUkymcxYlDhQPzHGisEVp8HwYWCVVVgEsrsuLnL74+5uqTF9Z+7cO/d8PO95zvO851BlZSV0XQcAMDOYGQBARDhX3JRmMDYZwLPMWAzGHACYIgwS46oBNBNwtOL8CwE8EkSEUCgE2rJlC2w2G4go8Zwo/bMDgnoG6gxLfAAAYvPDMCCszKTAMIAGAhrWnf15AAAMwwARIRKJgDZv3gy73Q4iAjPjxIr9VVOMRhbAYKB8zvrO0llrfEsdKwLZek6YAPSFvtSu3GtLawu0ZJ6625SHGBQB1T88t6MxvopgMAjaunUrdF0HM+P4yv27DMYeJmB1RqW3Jnf3tQX2p0L4P9EXuqEd7PmDp+XuMU9sRbvXnnt1TxxACgoKYLVacbzsQDUJGkSATe6qi28uPtzusM6Kxie6NHLGUX3lxVUNX9StPHnn4wy3njuUYcu6n2pNi66avcEXnByP/nv8aiaIyrqz2gO5A9+9FI1GIfn5+WhZdTAdjFMkwMvZOy7uWnTAOz3L4Yk71m3t69fdfTDoUGTBeHTUfiHQ6lo7Z2OXJvpDAChKe+aOCdKRKWxZ2+1qb3yyd3GYmRkQ7GQBVs99wfv6on3eR2k4PdTkDEbH7IuS8/svld/561PJS/pDk1/bzwx94pze7xc5v/H+YPY6r5BAkdrJzODTK46lE6PeYEJt7u+8j+OZwCBiEAgAoNgKJoEQf6PvNvdrXgtZoNhSf7q0KZ3B2AQmVMze0Jmt54S/DcDCVig2NcvEUGxJAE4Pl+YOr0iv6BRSIPAmBeBZAmHlE2sH4p1uhrq1s0MnnEQMBsf8wRASAICQQCCITN1X7/sOuc0kgOVp3/fPs2WHv+coG7gQOJUnLGsUCTxEjPzUohEA+NfIWUdtx0+efzA1kSSkIGyBAQNCKgHAEBAJ3u79U7kiAcWoem/gb5Fd33nrH3kp+SMWtuAB+GllMJxMjCx9QRgA3uiqL5kwHiTlpxb3smlfMDGYGPP1hcMAkJvs8ScpfdJspdj+MK6Pf+5+u29vyb4lR4+BGEziVESAkEpw6Av1OhUpHCz4qOXbzFWz4Ncdj/v/o08Lt92ODDgZDCEFJYoUGH4mzugP92puPTf0pD3H7wvfdFZdqSxnMtWjoGAAmG9fOLxjwesdjT2/XzIQ7ks3sycYMSEwGHNtWf5bkX5NkYCJBxUBXiGV0XHvosOt54Zey33j/K+8P33++vjnbiGJbbLE+J9SANAb6nJ2B79wcUwETAwQQ7fMjPzMvfP8ja87HUIKMOiaAqMZhrGmLdAy78eZrwwsTS0eObTs+IdtgVanxBUExqGbb5VzrIISGIoUXsmqbgEhJldCQWqRf27SvPAn/o8XmgLhZsUkR4ll37mhk3n94Z4OlzY/7NLcYZfm7o1z2zT4vsvUNSXqprBCkmiTFbPX90/fh8GIT2sf+zTPdDMf4dVnNg4z+E0ixsGeBs9jd5ViSgLHjCb/peaR+MD3d4/ZJg2llyuG2Vwy7QWAs8PNnn1f7vkGSGxAzE6mk+kxkx/p/4unffSCR0hAoL1EBCYiPNdWNcwkNQTCR7feWX6g+7f/A7I8rcw/U6UEe0Ndrhc/W7mtL9ztmqlSgstSS/zTJ28dalpOpkRryrwbhwBACgsLMWPGDOT4ll3qyeqAkJTdCF7P/CrUY/GkLL1rE+2hTbSH8+0Lb/WEuhzhyaA905blf9Vd/895WnZwLHrPevir/cvOB1oLYpTtLrm6oYGIMDExAaqtrUVKSgqYGSKCk0WHq5ikkWEWtNL0imv5qUW+RclLRjJsrhBAuH1/QL8R7HR4xy5nescuP23E6hOA6mLv+sb4uTw6Ogqqq6uDpmkQkcStorX4XRcM1FjZ+kvFFjCJKU1WpkNJJUqIMtX1RyLeX3JtQ0JRhmGYZ/L27duRnJycuFGISOJ9pqh5lrB6iYgqGOxRrOaa54DcZmKvkJxk8JHC9rKh+KVhOsD4+Dj+MwADIf8n5m4xGwAAAABJRU5ErkJggg==")}div.vis-network div.vis-edit-mode button.vis-button.vis-edit,div.vis-network div.vis-manipulation button.vis-button.vis-edit{background-image:url("data:image/png;base64,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/GMzLdsAAEEOaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/Pgo8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjUtYzAyMSA3OS4xNTQ5MTEsIDIwMTMvMTAvMjktMTE6NDc6MTYgICAgICAgICI+CiAgIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgICAgICAgICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICAgICAgICAgICB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIKICAgICAgICAgICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgICAgICAgICAgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiCiAgICAgICAgICAgIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOmV4aWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vZXhpZi8xLjAvIj4KICAgICAgICAgPHhtcDpDcmVhdG9yVG9vbD5BZG9iZSBQaG90b3Nob3AgQ0MgKFdpbmRvd3MpPC94bXA6Q3JlYXRvclRvb2w+CiAgICAgICAgIDx4bXA6Q3JlYXRlRGF0ZT4yMDE0LTAxLTIyVDE5OjI0OjUxKzAxOjAwPC94bXA6Q3JlYXRlRGF0ZT4KICAgICAgICAgPHhtcDpNZXRhZGF0YURhdGU+MjAxNC0wMi0wNVQxNDoxMjoyNSswMTowMDwveG1wOk1ldGFkYXRhRGF0ZT4KICAgICAgICAgPHhtcDpNb2RpZnlEYXRlPjIwMTQtMDItMDVUMTQ6MTI6MjUrMDE6MDA8L3htcDpNb2RpZnlEYXRlPgogICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3BuZzwvZGM6Zm9ybWF0PgogICAgICAgICA8eG1wTU06SW5zdGFuY2VJRD54bXAuaWlkOjY5OTM3ZGZjLTJjNzQtYTU0YS05OTIzLTQyMmZhNDNkMjljNDwveG1wTU06SW5zdGFuY2VJRD4KICAgICAgICAgPHhtcE1NOkRvY3VtZW50SUQ+eG1wLmRpZDpFQTc2MkY5Njc0ODNFMzExOTQ4QkQxM0UyQkU3OTlBMTwveG1wTU06RG9jdW1lbnRJRD4KICAgICAgICAgPHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD54bXAuZGlkOjczQjYyQUFEOTE4M0UzMTE5NDhCRDEzRTJCRTc5OUExPC94bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ+CiAgICAgICAgIDx4bXBNTTpIaXN0b3J5PgogICAgICAgICAgICA8cmRmOlNlcT4KICAgICAgICAgICAgICAgPHJkZjpsaSByZGY6cGFyc2VUeXBlPSJSZXNvdXJjZSI+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDphY3Rpb24+Y3JlYXRlZDwvc3RFdnQ6YWN0aW9uPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6aW5zdGFuY2VJRD54bXAuaWlkOjczQjYyQUFEOTE4M0UzMTE5NDhCRDEzRTJCRTc5OUExPC9zdEV2dDppbnN0YW5jZUlEPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6d2hlbj4yMDE0LTAxLTIyVDE5OjI0OjUxKzAxOjAwPC9zdEV2dDp3aGVuPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6c29mdHdhcmVBZ2VudD5BZG9iZSBQaG90b3Nob3AgQ1M2IChXaW5kb3dzKTwvc3RFdnQ6c29mdHdhcmVBZ2VudD4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6YWN0aW9uPnNhdmVkPC9zdEV2dDphY3Rpb24+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDppbnN0YW5jZUlEPnhtcC5paWQ6RUE2MEEyNEUxOTg0RTMxMUFEQUZFRkU2RUMzMzNFMDM8L3N0RXZ0Omluc3RhbmNlSUQ+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDp3aGVuPjIwMTQtMDEtMjNUMTk6MTg6MDcrMDE6MDA8L3N0RXZ0OndoZW4+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDpzb2Z0d2FyZUFnZW50PkFkb2JlIFBob3Rvc2hvcCBDUzYgKFdpbmRvd3MpPC9zdEV2dDpzb2Z0d2FyZUFnZW50PgogICAgICAgICAgICAgICAgICA8c3RFdnQ6Y2hhbmdlZD4vPC9zdEV2dDpjaGFuZ2VkPgogICAgICAgICAgICAgICA8L3JkZjpsaT4KICAgICAgICAgICAgICAgPHJkZjpsaSByZGY6cGFyc2VUeXBlPSJSZXNvdXJjZSI+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDphY3Rpb24+c2F2ZWQ8L3N0RXZ0OmFjdGlvbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0Omluc3RhbmNlSUQ+eG1wLmlpZDozOWNhNzE5ZC03YzNlLTUyNGEtYmY1NS03NGVmMmM1MzE0YTc8L3N0RXZ0Omluc3RhbmNlSUQ+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDp3aGVuPjIwMTQtMDItMDVUMTQ6MTI6MjUrMDE6MDA8L3N0RXZ0OndoZW4+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDpzb2Z0d2FyZUFnZW50PkFkb2JlIFBob3Rvc2hvcCBDQyAoV2luZG93cyk8L3N0RXZ0OnNvZnR3YXJlQWdlbnQ+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDpjaGFuZ2VkPi88L3N0RXZ0OmNoYW5nZWQ+CiAgICAgICAgICAgICAgIDwvcmRmOmxpPgogICAgICAgICAgICAgICA8cmRmOmxpIHJkZjpwYXJzZVR5cGU9IlJlc291cmNlIj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmFjdGlvbj5jb252ZXJ0ZWQ8L3N0RXZ0OmFjdGlvbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OnBhcmFtZXRlcnM+ZnJvbSBhcHBsaWNhdGlvbi92bmQuYWRvYmUucGhvdG9zaG9wIHRvIGltYWdlL3BuZzwvc3RFdnQ6cGFyYW1ldGVycz4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6YWN0aW9uPmRlcml2ZWQ8L3N0RXZ0OmFjdGlvbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OnBhcmFtZXRlcnM+Y29udmVydGVkIGZyb20gYXBwbGljYXRpb24vdm5kLmFkb2JlLnBob3Rvc2hvcCB0byBpbWFnZS9wbmc8L3N0RXZ0OnBhcmFtZXRlcnM+CiAgICAgICAgICAgICAgIDwvcmRmOmxpPgogICAgICAgICAgICAgICA8cmRmOmxpIHJkZjpwYXJzZVR5cGU9IlJlc291cmNlIj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmFjdGlvbj5zYXZlZDwvc3RFdnQ6YWN0aW9uPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6aW5zdGFuY2VJRD54bXAuaWlkOjY5OTM3ZGZjLTJjNzQtYTU0YS05OTIzLTQyMmZhNDNkMjljNDwvc3RFdnQ6aW5zdGFuY2VJRD4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OndoZW4+MjAxNC0wMi0wNVQxNDoxMjoyNSswMTowMDwvc3RFdnQ6d2hlbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OnNvZnR3YXJlQWdlbnQ+QWRvYmUgUGhvdG9zaG9wIENDIChXaW5kb3dzKTwvc3RFdnQ6c29mdHdhcmVBZ2VudD4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmNoYW5nZWQ+Lzwvc3RFdnQ6Y2hhbmdlZD4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgIDwvcmRmOlNlcT4KICAgICAgICAgPC94bXBNTTpIaXN0b3J5PgogICAgICAgICA8eG1wTU06RGVyaXZlZEZyb20gcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICA8c3RSZWY6aW5zdGFuY2VJRD54bXAuaWlkOjM5Y2E3MTlkLTdjM2UtNTI0YS1iZjU1LTc0ZWYyYzUzMTRhNzwvc3RSZWY6aW5zdGFuY2VJRD4KICAgICAgICAgICAgPHN0UmVmOmRvY3VtZW50SUQ+eG1wLmRpZDpFQTc2MkY5Njc0ODNFMzExOTQ4QkQxM0UyQkU3OTlBMTwvc3RSZWY6ZG9jdW1lbnRJRD4KICAgICAgICAgICAgPHN0UmVmOm9yaWdpbmFsRG9jdW1lbnRJRD54bXAuZGlkOjczQjYyQUFEOTE4M0UzMTE5NDhCRDEzRTJCRTc5OUExPC9zdFJlZjpvcmlnaW5hbERvY3VtZW50SUQ+CiAgICAgICAgIDwveG1wTU06RGVyaXZlZEZyb20+CiAgICAgICAgIDxwaG90b3Nob3A6Q29sb3JNb2RlPjM8L3Bob3Rvc2hvcDpDb2xvck1vZGU+CiAgICAgICAgIDxwaG90b3Nob3A6SUNDUHJvZmlsZT5zUkdCIElFQzYxOTY2LTIuMTwvcGhvdG9zaG9wOklDQ1Byb2ZpbGU+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgICAgIDx0aWZmOlhSZXNvbHV0aW9uPjcyMDA5MC8xMDAwMDwvdGlmZjpYUmVzb2x1dGlvbj4KICAgICAgICAgPHRpZmY6WVJlc29sdXRpb24+NzIwMDkwLzEwMDAwPC90aWZmOllSZXNvbHV0aW9uPgogICAgICAgICA8dGlmZjpSZXNvbHV0aW9uVW5pdD4yPC90aWZmOlJlc29sdXRpb25Vbml0PgogICAgICAgICA8ZXhpZjpDb2xvclNwYWNlPjE8L2V4aWY6Q29sb3JTcGFjZT4KICAgICAgICAgPGV4aWY6UGl4ZWxYRGltZW5zaW9uPjI0PC9leGlmOlBpeGVsWERpbWVuc2lvbj4KICAgICAgICAgPGV4aWY6UGl4ZWxZRGltZW5zaW9uPjI0PC9leGlmOlBpeGVsWURpbWVuc2lvbj4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgCjw/eHBhY2tldCBlbmQ9InciPz4ykninAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAYpSURBVHjafFZtTFvnFX7Oea+NudiY2Hwam4CBlgQwXdKREDKUoYg0jbRJ29RJ2VZ1mjRFUxSpA3VTfkzJfkQbS7spU6rtx5Z2UtppScjaHxvLuiatWi2jLEoMIUDCh23g2gbj7+tPuPvhOurawPl1dc99n+c55z33fV46ceIEZFkGADAziAgAQERoe/9ZK4GPM/AcgbsIXAcABCgMvkfAqAa89eDoJyF8LogIqqqChoaGYDAYHr8kItS8uc8iIH6iAa9IkAo5EAQX8pqmgUVBCBggYFgDhv0/GAsBgKZpICJkMhnQ4OAgZFkGEYGZUXmp+0cS+CKBwWA0DVRPOg5Zl2q6zaHyJlnVAMQXVTkwHrUqH0Xsvn+tdQAAMQDgpPLS2MViFY8rkGUZzIzaS/t/xqCzGggtz9e697zsnKhoLUtim4jOq/LE6x7X0nsh16dEZ5a/O3a2SCAOHjwInU6Hujd6ThJ4mCDQ+b2G232v7v6vwarPbQn8MGlMr+X0kpE3Wr5Zt5hL5HPhqYSdQIfKJ+yhxDPKWC6Xg+jt7UXD5b5KBt1kCHS85Ljd8/On3NupfnhFaZj4rWff1B98B1R/hnUmKd36bdtCNl4g0en4edNE/cXwLq8qMTMIPAQwmo/WuHvObA8+9c58k/dKtD0TyZWXN5YGA7ej7epKxspM//7SoNOdWc/Jyq2wiwhDzPxT8cP0jys3VMM7OmL0/77zn4Ydui3b8uiK0jD7RrA77c9Wd57cefPpF+2T6bWsFPWkaiPTCWvTsZpHFU+XrS+8G3AR08F6X+1FJvBxQQzHQOWk2SmrW4FPX/U2LVwPuDZj+fJKl2khPpeyAqA9rzR/YqwuiWXX8taN/CabGkrVuq9YJlkQQDjOAJ5jAhz9Vt9W4N5/rNp8I+vtMV/aZm4zLnUNNt0urdYnF68HWoJj4Wo1mLGUNRr8LEgDgNqeCh8xQIKOsgC7iAjVe83rT9zQa8uNM28u70kspessu8q8zq/V3NcZpVzb9+0zmVhOvvvrhaMVzrJg0zeq7xMVCCwdpnWSGBqjUyJwLTFgbvxie3w31uoWR1Y74r60rdxZqrR8q85t2W2MGCp12bm/KC3hyaSTiMhxuGrKcahqpbjOaDOoEhOEoFqJQCCJvqA85I6bfTdDjQlf2lbxVNlS6wt19yy7jRHZZlDnrinNj/6sHMhnNw2Ogco7O79e5fm/xQywRBBCEAuwn4gQ96bkYj4Vyuq9N1Z3Bj4Od5bs0MXt/dZZ21ctiqFan174q985P+Lfp+U1g7XDON/1ctP458WlVjLyJhOISZE0wM0S1QfuRC3lTjkJAKKEtNC9eIOhSh9xHLZOJRZTFuXDsEoStLkR/768ummsaJG9Pb9oe+9J+xaeSVokiQDSJphAo5uaBuWjiKP4QTqS1cUWU7ayesN66wu22frD1vmVW6GW6T8u9eVjGyZzs+w78Nqu0a2mbvVu1KEJQAgeZRL0liQYyx+GOmKeQpu0rMYsAJPNEFGD2dLodLIy6c9Ys7G8yeSUl3tf2/X3rcBVJSOv34l3sCBogi7z1LH/rBHjl4IJ93/ncQFAnjeImJD0Z8zuCwu9q3djDXqTlAKID5xv+9t2R8n8VcUFBljQ8Gyfe40BYBM4DwDLt8Kue79ZcFkbzfEdbUbv+oN4c9KTtsfm1MbYQqqh+2zrVZYKs/7Ef+byimt1POYiJhDhPBFBIiIEXhxfs7/dfYoIF+auBfYTE/pebx/V8hqBP2ODvD34yvuh/WCAmU75Bx6sIgaI/v5+6PV6JLqUsYr7dpDAoehs0h73pHTWrvKgThYbRSt9UmSjef3MpaUvBz4O72UmADgTOPJguGiZor+/HyUlJWBmJFz+D8xTtlUiOpbwpmrmrweeSXrT+g11k4SBN3RGKUcAVCVdFhyP1nreDbY//NPyEXUlU/Pp4XYycGT6V0Ux2WwWdO7cOZSWlkII8diX7SPPNgDaKdbxoNAxwATBAEkEEgSWCEQAqPAMwqvMdCEwMO0tVqZpWsGTT58+DaPR+PhGIYQAAAgh0P7B3ioW/B0iGiCGiwXbCuOHFSJys6AbYFye2T+xWhT3WYJEIoH/DQBMw3kes8OJPgAAAABJRU5ErkJggg==")}div.vis-network div.vis-edit-mode button.vis-button.vis-edit.vis-edit-mode{background-color:#fcfcfc;border:1px solid #ccc}div.vis-network div.vis-manipulation button.vis-button.vis-connect{background-image:url("data:image/png;base64,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/GMzLdsAAEEOaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/Pgo8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjUtYzAyMSA3OS4xNTQ5MTEsIDIwMTMvMTAvMjktMTE6NDc6MTYgICAgICAgICI+CiAgIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgICAgICAgICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICAgICAgICAgICB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIKICAgICAgICAgICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgICAgICAgICAgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiCiAgICAgICAgICAgIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOmV4aWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vZXhpZi8xLjAvIj4KICAgICAgICAgPHhtcDpDcmVhdG9yVG9vbD5BZG9iZSBQaG90b3Nob3AgQ0MgKFdpbmRvd3MpPC94bXA6Q3JlYXRvclRvb2w+CiAgICAgICAgIDx4bXA6Q3JlYXRlRGF0ZT4yMDE0LTAxLTIyVDE5OjI0OjUxKzAxOjAwPC94bXA6Q3JlYXRlRGF0ZT4KICAgICAgICAgPHhtcDpNZXRhZGF0YURhdGU+MjAxNC0wMi0wNFQxNDozODo1NyswMTowMDwveG1wOk1ldGFkYXRhRGF0ZT4KICAgICAgICAgPHhtcDpNb2RpZnlEYXRlPjIwMTQtMDItMDRUMTQ6Mzg6NTcrMDE6MDA8L3htcDpNb2RpZnlEYXRlPgogICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3BuZzwvZGM6Zm9ybWF0PgogICAgICAgICA8eG1wTU06SW5zdGFuY2VJRD54bXAuaWlkOjlmYjUwMDU0LWE3ODEtMWQ0OC05ZTllLTU2ZWQ5YzhlYjdjNjwveG1wTU06SW5zdGFuY2VJRD4KICAgICAgICAgPHhtcE1NOkRvY3VtZW50SUQ+eG1wLmRpZDpFQTc2MkY5Njc0ODNFMzExOTQ4QkQxM0UyQkU3OTlBMTwveG1wTU06RG9jdW1lbnRJRD4KICAgICAgICAgPHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD54bXAuZGlkOjczQjYyQUFEOTE4M0UzMTE5NDhCRDEzRTJCRTc5OUExPC94bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ+CiAgICAgICAgIDx4bXBNTTpIaXN0b3J5PgogICAgICAgICAgICA8cmRmOlNlcT4KICAgICAgICAgICAgICAgPHJkZjpsaSByZGY6cGFyc2VUeXBlPSJSZXNvdXJjZSI+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDphY3Rpb24+Y3JlYXRlZDwvc3RFdnQ6YWN0aW9uPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6aW5zdGFuY2VJRD54bXAuaWlkOjczQjYyQUFEOTE4M0UzMTE5NDhCRDEzRTJCRTc5OUExPC9zdEV2dDppbnN0YW5jZUlEPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6d2hlbj4yMDE0LTAxLTIyVDE5OjI0OjUxKzAxOjAwPC9zdEV2dDp3aGVuPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6c29mdHdhcmVBZ2VudD5BZG9iZSBQaG90b3Nob3AgQ1M2IChXaW5kb3dzKTwvc3RFdnQ6c29mdHdhcmVBZ2VudD4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6YWN0aW9uPnNhdmVkPC9zdEV2dDphY3Rpb24+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDppbnN0YW5jZUlEPnhtcC5paWQ6RUE2MEEyNEUxOTg0RTMxMUFEQUZFRkU2RUMzMzNFMDM8L3N0RXZ0Omluc3RhbmNlSUQ+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDp3aGVuPjIwMTQtMDEtMjNUMTk6MTg6MDcrMDE6MDA8L3N0RXZ0OndoZW4+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDpzb2Z0d2FyZUFnZW50PkFkb2JlIFBob3Rvc2hvcCBDUzYgKFdpbmRvd3MpPC9zdEV2dDpzb2Z0d2FyZUFnZW50PgogICAgICAgICAgICAgICAgICA8c3RFdnQ6Y2hhbmdlZD4vPC9zdEV2dDpjaGFuZ2VkPgogICAgICAgICAgICAgICA8L3JkZjpsaT4KICAgICAgICAgICAgICAgPHJkZjpsaSByZGY6cGFyc2VUeXBlPSJSZXNvdXJjZSI+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDphY3Rpb24+c2F2ZWQ8L3N0RXZ0OmFjdGlvbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0Omluc3RhbmNlSUQ+eG1wLmlpZDo3ZWRhMjI0MC0yYTQxLTNlNDQtYWM2My1iNzNiYTE5OWI3Y2E8L3N0RXZ0Omluc3RhbmNlSUQ+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDp3aGVuPjIwMTQtMDItMDRUMTQ6Mzg6NTcrMDE6MDA8L3N0RXZ0OndoZW4+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDpzb2Z0d2FyZUFnZW50PkFkb2JlIFBob3Rvc2hvcCBDQyAoV2luZG93cyk8L3N0RXZ0OnNvZnR3YXJlQWdlbnQ+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDpjaGFuZ2VkPi88L3N0RXZ0OmNoYW5nZWQ+CiAgICAgICAgICAgICAgIDwvcmRmOmxpPgogICAgICAgICAgICAgICA8cmRmOmxpIHJkZjpwYXJzZVR5cGU9IlJlc291cmNlIj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmFjdGlvbj5jb252ZXJ0ZWQ8L3N0RXZ0OmFjdGlvbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OnBhcmFtZXRlcnM+ZnJvbSBhcHBsaWNhdGlvbi92bmQuYWRvYmUucGhvdG9zaG9wIHRvIGltYWdlL3BuZzwvc3RFdnQ6cGFyYW1ldGVycz4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6YWN0aW9uPmRlcml2ZWQ8L3N0RXZ0OmFjdGlvbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OnBhcmFtZXRlcnM+Y29udmVydGVkIGZyb20gYXBwbGljYXRpb24vdm5kLmFkb2JlLnBob3Rvc2hvcCB0byBpbWFnZS9wbmc8L3N0RXZ0OnBhcmFtZXRlcnM+CiAgICAgICAgICAgICAgIDwvcmRmOmxpPgogICAgICAgICAgICAgICA8cmRmOmxpIHJkZjpwYXJzZVR5cGU9IlJlc291cmNlIj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmFjdGlvbj5zYXZlZDwvc3RFdnQ6YWN0aW9uPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6aW5zdGFuY2VJRD54bXAuaWlkOjlmYjUwMDU0LWE3ODEtMWQ0OC05ZTllLTU2ZWQ5YzhlYjdjNjwvc3RFdnQ6aW5zdGFuY2VJRD4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OndoZW4+MjAxNC0wMi0wNFQxNDozODo1NyswMTowMDwvc3RFdnQ6d2hlbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OnNvZnR3YXJlQWdlbnQ+QWRvYmUgUGhvdG9zaG9wIENDIChXaW5kb3dzKTwvc3RFdnQ6c29mdHdhcmVBZ2VudD4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmNoYW5nZWQ+Lzwvc3RFdnQ6Y2hhbmdlZD4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgIDwvcmRmOlNlcT4KICAgICAgICAgPC94bXBNTTpIaXN0b3J5PgogICAgICAgICA8eG1wTU06RGVyaXZlZEZyb20gcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICA8c3RSZWY6aW5zdGFuY2VJRD54bXAuaWlkOjdlZGEyMjQwLTJhNDEtM2U0NC1hYzYzLWI3M2JhMTk5YjdjYTwvc3RSZWY6aW5zdGFuY2VJRD4KICAgICAgICAgICAgPHN0UmVmOmRvY3VtZW50SUQ+eG1wLmRpZDpFQTc2MkY5Njc0ODNFMzExOTQ4QkQxM0UyQkU3OTlBMTwvc3RSZWY6ZG9jdW1lbnRJRD4KICAgICAgICAgICAgPHN0UmVmOm9yaWdpbmFsRG9jdW1lbnRJRD54bXAuZGlkOjczQjYyQUFEOTE4M0UzMTE5NDhCRDEzRTJCRTc5OUExPC9zdFJlZjpvcmlnaW5hbERvY3VtZW50SUQ+CiAgICAgICAgIDwveG1wTU06RGVyaXZlZEZyb20+CiAgICAgICAgIDxwaG90b3Nob3A6Q29sb3JNb2RlPjM8L3Bob3Rvc2hvcDpDb2xvck1vZGU+CiAgICAgICAgIDxwaG90b3Nob3A6SUNDUHJvZmlsZT5zUkdCIElFQzYxOTY2LTIuMTwvcGhvdG9zaG9wOklDQ1Byb2ZpbGU+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgICAgIDx0aWZmOlhSZXNvbHV0aW9uPjcyMDA5MC8xMDAwMDwvdGlmZjpYUmVzb2x1dGlvbj4KICAgICAgICAgPHRpZmY6WVJlc29sdXRpb24+NzIwMDkwLzEwMDAwPC90aWZmOllSZXNvbHV0aW9uPgogICAgICAgICA8dGlmZjpSZXNvbHV0aW9uVW5pdD4yPC90aWZmOlJlc29sdXRpb25Vbml0PgogICAgICAgICA8ZXhpZjpDb2xvclNwYWNlPjE8L2V4aWY6Q29sb3JTcGFjZT4KICAgICAgICAgPGV4aWY6UGl4ZWxYRGltZW5zaW9uPjI0PC9leGlmOlBpeGVsWERpbWVuc2lvbj4KICAgICAgICAgPGV4aWY6UGl4ZWxZRGltZW5zaW9uPjI0PC9leGlmOlBpeGVsWURpbWVuc2lvbj4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgCjw/eHBhY2tldCBlbmQ9InciPz4ubxs+AAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAUtSURBVHjajJZ/bNT1Gcdfz/P53PV6B4W7VltLqdAaplIOiMOoyxxJCSs/Gv/yB4gzJroAosmmDklwkYWR0bQsdmkykoojTpcsWYLxD/lRZdMQkTHRtkLZRqG0tIVe7662vTu43n32x/VKZ/jh89cn38/zvN7P5/l88zwf2blzJz6fDwARQUSm1n8s31CM0/VAnbNmsUPuAsDpgEO+Bg4C7//iyv5hvmMiQiqVQpqamvB6vVNwEeG1JZtCBrYi/MrkAwDNgjhwAlbzICBLA0rDb0+/839C6XQaaWxspLCw8Dp86cbNmqVFJQddE6KzdjZ9D89g+B6fSyCOcyn1nxil+O9xKg5HqWFSHGXLjrP7W/ICqVQK2bNnDz6fDxFh65KNvxbHDhF4rJj2bXPo+IGfcW5h5xL4f99P+FCEMIAob75x9t0dAMlkElNXV4e1lteXbNqiQoMaeOFOjrdU868SD2luYyEP6dUh+sYmSHeOU6GO5Z8VLx5+NNZxIpPJ5AS2L3upROCoCvz8Lo7vnkf77cAHhpiz/zIL9vWz8L8p/NvupmM0Q7pjnAoLqz8tDrc8MnQqYVUVhVdF4LEg7b+rvDn8wDDlH0WoPpukLJImSBaMwjcJqmwWts2jPZLG/8kwYVFeVdXXZcFf4yVDc2cNKfBFmD9X+0ncCP58F48eG+Feo2CAUkvs4dl0V/uJvdXLiiV+ut++n7YLSfxPfMMG54ChzB3WIesVWB2i82bw1AR6fJR7C4VsfYiv6u/k3A9nEgP4zXke8DiYHyAOMK+QxPIgnZ9GqSHr1itQJ8DK2fTerDQ+S/bHRXQJaHSCwNIZ2Xh+7+S3VAmwNMBA/tuPZtErgKquUmdMWIFlRURvdamRNEXGwIWrlP47pTMzLiunxghGMwTLvcTWlHAp77s4QNSrYMQtss6ZMgWqCm5cHoDHO1nbk6K8zEN8+3zatv2Hn1b59EqJZdxmYUERg9P9KwpIiAOTdWUWBXuLzB/vZG3P1Un4PNp2d1MbmyD45TWCxuCsQm0x56bHGHFYEZwxok7toAA9Sfw3hCcoL/NOwi9QO5wmWO1j4JEgZxTkodmcWRGkf3pcX0r8xoAaBixKu4U5/xwndM+0tpAvS6mP+PZK2nb1UBvPEKwKMLDvPj4ESGc55lGy303sdJKQdZB2rkMdctAB/4gzN+/Q2ENNd4LyUi/xN+bTtquX2thk5nk4wI3gAF+OMNcA1nFQDfK+BY5GqbkwWabTY5QZhXWlnNx1ntrY1Rz87fuvw29m/Sn8J+PUGAFj5T19baA1IspuBZp7cx1x4SwG1cEf+lgRSROs8jGwb+Ht4QB/GSSsAhYano39LWIBxNEIbP14hPDuiyS2VtJuHXQlKKvxM/jiXDq/D/xPlwifGMkJZB2NIoKpr69nxeiZxLHicFSFVWfGqBidIP3LSjrWltD94CyufF/4kQgPuVz2Lz93+dDRa9eu5QQ8Hg8/iXee+Dy4CKMs7xqn4nwKz9IirhQqmVuB42m8ey+x7LMoD6iAON782eChhqmRuXfvXgKBAKqKqtI0/8nNKrQI4BVYXkzHgzPpC88gWuHL/caXrhLoGiN0apSKr0ZZRBZM7q2w5ZnLR1oAnHOMjY0hra2tFBQUYIyZmstvVT1Z6eDlAuEVq7merxmwueNPDXy9PvybjKP5mctHLk4/XTKZRJqbm/H7/VNw1VyEMYbW4FN3WNWnnchKoy5sHeVGBRX6VWi3ymFx7r11Ix8MTX/y5C2RSPC/AQB61erowbpqSwAAAABJRU5ErkJggg==")}div.vis-network div.vis-manipulation button.vis-button.vis-delete{background-image:url("data:image/png;base64,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/GMzLdsAAEEOaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/Pgo8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjUtYzAyMSA3OS4xNTQ5MTEsIDIwMTMvMTAvMjktMTE6NDc6MTYgICAgICAgICI+CiAgIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgICAgICAgICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICAgICAgICAgICB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIKICAgICAgICAgICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgICAgICAgICAgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiCiAgICAgICAgICAgIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOmV4aWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vZXhpZi8xLjAvIj4KICAgICAgICAgPHhtcDpDcmVhdG9yVG9vbD5BZG9iZSBQaG90b3Nob3AgQ0MgKFdpbmRvd3MpPC94bXA6Q3JlYXRvclRvb2w+CiAgICAgICAgIDx4bXA6Q3JlYXRlRGF0ZT4yMDE0LTAxLTIyVDE5OjI0OjUxKzAxOjAwPC94bXA6Q3JlYXRlRGF0ZT4KICAgICAgICAgPHhtcDpNZXRhZGF0YURhdGU+MjAxNC0wMi0wNFQxNDo0MTowNCswMTowMDwveG1wOk1ldGFkYXRhRGF0ZT4KICAgICAgICAgPHhtcDpNb2RpZnlEYXRlPjIwMTQtMDItMDRUMTQ6NDE6MDQrMDE6MDA8L3htcDpNb2RpZnlEYXRlPgogICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3BuZzwvZGM6Zm9ybWF0PgogICAgICAgICA8eG1wTU06SW5zdGFuY2VJRD54bXAuaWlkOjc3NDkzYmUxLTEyZGItOTg0NC1iNDYyLTg2NGVmNGIzMzM3MTwveG1wTU06SW5zdGFuY2VJRD4KICAgICAgICAgPHhtcE1NOkRvY3VtZW50SUQ+eG1wLmRpZDpFQTc2MkY5Njc0ODNFMzExOTQ4QkQxM0UyQkU3OTlBMTwveG1wTU06RG9jdW1lbnRJRD4KICAgICAgICAgPHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD54bXAuZGlkOjczQjYyQUFEOTE4M0UzMTE5NDhCRDEzRTJCRTc5OUExPC94bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ+CiAgICAgICAgIDx4bXBNTTpIaXN0b3J5PgogICAgICAgICAgICA8cmRmOlNlcT4KICAgICAgICAgICAgICAgPHJkZjpsaSByZGY6cGFyc2VUeXBlPSJSZXNvdXJjZSI+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDphY3Rpb24+Y3JlYXRlZDwvc3RFdnQ6YWN0aW9uPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6aW5zdGFuY2VJRD54bXAuaWlkOjczQjYyQUFEOTE4M0UzMTE5NDhCRDEzRTJCRTc5OUExPC9zdEV2dDppbnN0YW5jZUlEPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6d2hlbj4yMDE0LTAxLTIyVDE5OjI0OjUxKzAxOjAwPC9zdEV2dDp3aGVuPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6c29mdHdhcmVBZ2VudD5BZG9iZSBQaG90b3Nob3AgQ1M2IChXaW5kb3dzKTwvc3RFdnQ6c29mdHdhcmVBZ2VudD4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6YWN0aW9uPnNhdmVkPC9zdEV2dDphY3Rpb24+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDppbnN0YW5jZUlEPnhtcC5paWQ6RUE2MEEyNEUxOTg0RTMxMUFEQUZFRkU2RUMzMzNFMDM8L3N0RXZ0Omluc3RhbmNlSUQ+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDp3aGVuPjIwMTQtMDEtMjNUMTk6MTg6MDcrMDE6MDA8L3N0RXZ0OndoZW4+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDpzb2Z0d2FyZUFnZW50PkFkb2JlIFBob3Rvc2hvcCBDUzYgKFdpbmRvd3MpPC9zdEV2dDpzb2Z0d2FyZUFnZW50PgogICAgICAgICAgICAgICAgICA8c3RFdnQ6Y2hhbmdlZD4vPC9zdEV2dDpjaGFuZ2VkPgogICAgICAgICAgICAgICA8L3JkZjpsaT4KICAgICAgICAgICAgICAgPHJkZjpsaSByZGY6cGFyc2VUeXBlPSJSZXNvdXJjZSI+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDphY3Rpb24+c2F2ZWQ8L3N0RXZ0OmFjdGlvbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0Omluc3RhbmNlSUQ+eG1wLmlpZDowNmE3NWYwMy04MDdhLWUzNGYtYjk1Zi1jZGU2MjM0Mzg4OGY8L3N0RXZ0Omluc3RhbmNlSUQ+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDp3aGVuPjIwMTQtMDItMDRUMTQ6NDE6MDQrMDE6MDA8L3N0RXZ0OndoZW4+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDpzb2Z0d2FyZUFnZW50PkFkb2JlIFBob3Rvc2hvcCBDQyAoV2luZG93cyk8L3N0RXZ0OnNvZnR3YXJlQWdlbnQ+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDpjaGFuZ2VkPi88L3N0RXZ0OmNoYW5nZWQ+CiAgICAgICAgICAgICAgIDwvcmRmOmxpPgogICAgICAgICAgICAgICA8cmRmOmxpIHJkZjpwYXJzZVR5cGU9IlJlc291cmNlIj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmFjdGlvbj5jb252ZXJ0ZWQ8L3N0RXZ0OmFjdGlvbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OnBhcmFtZXRlcnM+ZnJvbSBhcHBsaWNhdGlvbi92bmQuYWRvYmUucGhvdG9zaG9wIHRvIGltYWdlL3BuZzwvc3RFdnQ6cGFyYW1ldGVycz4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6YWN0aW9uPmRlcml2ZWQ8L3N0RXZ0OmFjdGlvbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OnBhcmFtZXRlcnM+Y29udmVydGVkIGZyb20gYXBwbGljYXRpb24vdm5kLmFkb2JlLnBob3Rvc2hvcCB0byBpbWFnZS9wbmc8L3N0RXZ0OnBhcmFtZXRlcnM+CiAgICAgICAgICAgICAgIDwvcmRmOmxpPgogICAgICAgICAgICAgICA8cmRmOmxpIHJkZjpwYXJzZVR5cGU9IlJlc291cmNlIj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmFjdGlvbj5zYXZlZDwvc3RFdnQ6YWN0aW9uPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6aW5zdGFuY2VJRD54bXAuaWlkOjc3NDkzYmUxLTEyZGItOTg0NC1iNDYyLTg2NGVmNGIzMzM3MTwvc3RFdnQ6aW5zdGFuY2VJRD4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OndoZW4+MjAxNC0wMi0wNFQxNDo0MTowNCswMTowMDwvc3RFdnQ6d2hlbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OnNvZnR3YXJlQWdlbnQ+QWRvYmUgUGhvdG9zaG9wIENDIChXaW5kb3dzKTwvc3RFdnQ6c29mdHdhcmVBZ2VudD4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmNoYW5nZWQ+Lzwvc3RFdnQ6Y2hhbmdlZD4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgIDwvcmRmOlNlcT4KICAgICAgICAgPC94bXBNTTpIaXN0b3J5PgogICAgICAgICA8eG1wTU06RGVyaXZlZEZyb20gcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICA8c3RSZWY6aW5zdGFuY2VJRD54bXAuaWlkOjA2YTc1ZjAzLTgwN2EtZTM0Zi1iOTVmLWNkZTYyMzQzODg4Zjwvc3RSZWY6aW5zdGFuY2VJRD4KICAgICAgICAgICAgPHN0UmVmOmRvY3VtZW50SUQ+eG1wLmRpZDpFQTc2MkY5Njc0ODNFMzExOTQ4QkQxM0UyQkU3OTlBMTwvc3RSZWY6ZG9jdW1lbnRJRD4KICAgICAgICAgICAgPHN0UmVmOm9yaWdpbmFsRG9jdW1lbnRJRD54bXAuZGlkOjczQjYyQUFEOTE4M0UzMTE5NDhCRDEzRTJCRTc5OUExPC9zdFJlZjpvcmlnaW5hbERvY3VtZW50SUQ+CiAgICAgICAgIDwveG1wTU06RGVyaXZlZEZyb20+CiAgICAgICAgIDxwaG90b3Nob3A6Q29sb3JNb2RlPjM8L3Bob3Rvc2hvcDpDb2xvck1vZGU+CiAgICAgICAgIDxwaG90b3Nob3A6SUNDUHJvZmlsZT5zUkdCIElFQzYxOTY2LTIuMTwvcGhvdG9zaG9wOklDQ1Byb2ZpbGU+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgICAgIDx0aWZmOlhSZXNvbHV0aW9uPjcyMDA5MC8xMDAwMDwvdGlmZjpYUmVzb2x1dGlvbj4KICAgICAgICAgPHRpZmY6WVJlc29sdXRpb24+NzIwMDkwLzEwMDAwPC90aWZmOllSZXNvbHV0aW9uPgogICAgICAgICA8dGlmZjpSZXNvbHV0aW9uVW5pdD4yPC90aWZmOlJlc29sdXRpb25Vbml0PgogICAgICAgICA8ZXhpZjpDb2xvclNwYWNlPjE8L2V4aWY6Q29sb3JTcGFjZT4KICAgICAgICAgPGV4aWY6UGl4ZWxYRGltZW5zaW9uPjI0PC9leGlmOlBpeGVsWERpbWVuc2lvbj4KICAgICAgICAgPGV4aWY6UGl4ZWxZRGltZW5zaW9uPjI0PC9leGlmOlBpeGVsWURpbWVuc2lvbj4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgCjw/eHBhY2tldCBlbmQ9InciPz4aYJzYAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAYGSURBVHjalJZ7UJTnFcZ/73m/72PdJY1RbhoQp6lkXRAvmIYxdCUadLVOozPNtGObap1JsKipjiShbdoRbeKEiQHpQK3xj0xa03aamTbaTGyAYV1QGeqFi+JyiZFLAlmESBkWRmS3fyzslGkmnZ5/v/M873Oe75zzvqqoqAibzQaAiKCUAkApRdHIK/NFsx2NR91nOSILADDoJyzNaM4xxbtvPHh0iC+JiYkJ1OHDh4mJiUEpFSXPv/ziPC28TIiXDCOSrAClQDSEpsCwJPIhrEBRQpiSytXlQwDhcBilFPfu3UMVFxdjt9ujFTzfcLBADCoEEAFr1ZbrrNjch2vtEImPBgHob7fTcWE+bVXJNJ/NiFQlEGLvieXHKmYqGB8fRx05cgSbzYaIsPvywV8pKFaA7fGtLTzz61YWpo/xVTHQbufsq5lcez9zWuWhk5mvFwMEg0H0+vXrMU2Tn1wp3CtCiQ5DjGd3A/m/v8IDCZP8r4iNmyRrWx/j/5qktykZpXKzAjVDVxPzGqemptDr1q1jX3NRnIJarcDKK2hgR2ULXRfncv7UYv7xpovhnhiW5Mz+kefeSKO6LJ1A1xzEuk/Ojm4mRibpuZaMZW3OCtRUND60NmiICCIUShisx7a2sLMiQn4s77uEQgIabnqdfHIlgT1/qQeg8vs5dHhdCNB1wYn3RIiC995j26stjAbsNH+YiZJCESnS1Y/XxIXu8r4YIPv/VkVs3CTnTy2ms34xro1+sp9po6sxlTu34ultmsPVvy6is86FCHgO+DDs49zpjufBpCG+seYOC9OHaTidieicb9ouVAhKtouAseI710ma7pLuqwmgYfHqAFt+6WdLoQ/LBl11Lm7VudAa8vb72PCin9TlAWIsGGhLACD+kSAZnusYBii1XQAPYWDllt6ov2lrBkDBR2+6Ofuak2//3M+G/T4wAAPW7fPhKfRTVeqk9qQbFKRmDUTxS3N7QYGYmwzCkqklBGlPDEcTNv+sg9tNCbTXuvBWujE0bHrZj9JE1B/wU1Pm5PwJN6YBS9a2kVvQEcWnrh5GTFD3lxkYkqRMgYQlwVldUvDnen73LHTUuqitdKM0eAr9AFQfd1J/yo2aJn+2sn4Wdn5qEFODJskgBIjx5T0uCrQA08pnIjS9PERDjPnfOKXAMEBECUoGEIHBj+2zkt76UQ6dXheGAev3+cg74Kf6uJPqcicbfuond7cPy4SOiy7+tD9nFvZurx00KOk3CNEC+mE+vjSPBc7IWqgqTaPT60IMcO/xsXGa3HfKjRgRdbl7/KDg0jtubje6aHj7c7J3dgLQ2zoPwwQ91SooOQdAW1VKVMHty0kA5Bb48BycJn/LjWFGbLv4thvvb53kFvjJ+XEdWkPfjQVR/CcNKYgGMc8JWt5Fa2j+MIPPuyI2pa4IoHSkt6vLIuRaQ9q32khzt4GCxtNu6k46GeiIR2lIfDQQsafPzq1LGRGL9Gk9d+vrwewvfHPQOoexQVjxdB/auk/zmaUMdsfz6bVUtIalT7bxveP1ZHh6GPDPYeSzeD69kcpIfxymFWLNrka+ljhBTWkWwz2JiJT84YHnz2iPx0P20PkmRF5i6HYiwZFJsn/YzdezbzE3cQibY5xV266z6RfXohakb+xB9CjanCD9qTbW7Grk4WV38VZm0l6dhQiEw9taHSuDqrS0FIfDwXM3X9mHMsvRAk/sauDpQy38P+GtzOTGB9mEpkD0C2dS8n8zOjqK9ng8WJZFU+JTjasGvaCNXPpvJBPoMlm0OoDNMfWVxONfWNSUPUZ7TUQ56tCZlPwSgMnJSVRpaSmxsbFE1raw82ZxAZZRQUiBYUKGp5UlOX2krBzmoUVjiIKhHge9rfPo+Wcy3ZeXIYASgL1/X5RfMXMvj46OosrLy7HZbGitUUohIuzoem0RofALaOsghgWGjky0MiJTL8b0lOvI8hN1DKXKP0jd3TNTWDgcJhgMoo4ePYrD4Yi+KmaeLlprnrtXFo9h/AAlG1AqE8yFmBrC+jO0bgH9EVpO/1F2Dc5g//OAsbEx/j0Af+USsQynL1UAAAAASUVORK5CYII=")}div.vis-network div.vis-edit-mode div.vis-label,div.vis-network div.vis-manipulation div.vis-label{line-height:25px;margin:0 0 0 23px}div.vis-network div.vis-manipulation div.vis-separator-line{background-color:#bdbdbd;display:inline-block;float:left;height:21px;margin:0 7px 0 15px;width:1px}');var t="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function C(g){return g&&g.__esModule&&Object.prototype.hasOwnProperty.call(g,"default")?g.default:g}var I,e,i,o,n,s,r,a,d,h,l,c,u,p,f,v,b={};function m(){if(e)return I;e=1;var g=function(g){return g&&g.Math===Math&&g};return I=g("object"==typeof globalThis&&globalThis)||g("object"==typeof window&&window)||g("object"==typeof self&&self)||g("object"==typeof t&&t)||g("object"==typeof I&&I)||function(){return this}()||Function("return this")()}function y(){return o?i:(o=1,i=function(g){try{return!!g()}catch(g){return!0}})}function w(){return s?n:(s=1,n=!y()(function(){var g=function(){}.bind();return"function"!=typeof g||g.hasOwnProperty("prototype")}))}function x(){if(a)return r;a=1;var g=w(),A=Function.prototype,t=A.apply,C=A.call;return r="object"==typeof Reflect&&Reflect.apply||(g?C.bind(t):function(){return C.apply(t,arguments)}),r}function E(){if(h)return d;h=1;var g=w(),A=Function.prototype,t=A.call,C=g&&A.bind.bind(t,t);return d=g?C:function(g){return function(){return t.apply(g,arguments)}},d}function O(){if(c)return l;c=1;var g=E(),A=g({}.toString),t=g("".slice);return l=function(g){return t(A(g),8,-1)},l}function T(){if(p)return u;p=1;var g=O(),A=E();return u=function(t){if("Function"===g(t))return A(t)}}function D(){if(v)return f;v=1;var g="object"==typeof document&&document.all;return f=void 0===g&&void 0!==g?function(A){return"function"==typeof A||A===g}:function(g){return"function"==typeof g}}var N,k,R,P,M={};function z(){return k?N:(k=1,N=!y()(function(){return 7!==Object.defineProperty({},1,{get:function(){return 7}})[1]}))}function B(){if(P)return R;P=1;var g=w(),A=Function.prototype.call;return R=g?A.bind(A):function(){return A.apply(A,arguments)},R}var Z,S,F,G,j,L,V,Y,W,Q,U,K,H,X,_,J,q,$,gg,Ag,tg,Cg,Ig,eg,ig,og,ng,sg,rg,ag,dg,hg,lg,cg,ug,pg,fg,vg={};function bg(){if(Z)return vg;Z=1;var g={}.propertyIsEnumerable,A=Object.getOwnPropertyDescriptor,t=A&&!g.call({1:2},1);return vg.f=t?function(g){var t=A(this,g);return!!t&&t.enumerable}:g,vg}function mg(){return F?S:(F=1,S=function(g,A){return{enumerable:!(1&g),configurable:!(2&g),writable:!(4&g),value:A}})}function yg(){if(j)return G;j=1;var g=E(),A=y(),t=O(),C=Object,I=g("".split);return G=A(function(){return!C("z").propertyIsEnumerable(0)})?function(g){return"String"===t(g)?I(g,""):C(g)}:C,G}function wg(){return V||(V=1,L=function(g){return null==g}),L}function xg(){if(W)return Y;W=1;var g=wg(),A=TypeError;return Y=function(t){if(g(t))throw new A("Can't call method on "+t);return t},Y}function Eg(){if(U)return Q;U=1;var g=yg(),A=xg();return Q=function(t){return g(A(t))},Q}function Og(){if(H)return K;H=1;var g=D();return K=function(A){return"object"==typeof A?null!==A:g(A)},K}function Tg(){return _?X:(_=1,X={})}function Dg(){if(q)return J;q=1;var g=Tg(),A=m(),t=D(),C=function(g){return t(g)?g:void 0};return J=function(t,I){return arguments.length<2?C(g[t])||C(A[t]):g[t]&&g[t][I]||A[t]&&A[t][I]},J}function Ng(){return gg?$:(gg=1,$=E()({}.isPrototypeOf))}function kg(){if(tg)return Ag;tg=1;var g=m().navigator,A=g&&g.userAgent;return Ag=A?String(A):""}function Rg(){if(Ig)return Cg;Ig=1;var g,A,t=m(),C=kg(),I=t.process,e=t.Deno,i=I&&I.versions||e&&e.version,o=i&&i.v8;return o&&(A=(g=o.split("."))[0]>0&&g[0]<4?1:+(g[0]+g[1])),!A&&C&&(!(g=C.match(/Edge\/(\d+)/))||g[1]>=74)&&(g=C.match(/Chrome\/(\d+)/))&&(A=+g[1]),Cg=A}function Pg(){if(ig)return eg;ig=1;var g=Rg(),A=y(),t=m().String;return eg=!!Object.getOwnPropertySymbols&&!A(function(){var A=Symbol("symbol detection");return!t(A)||!(Object(A)instanceof Symbol)||!Symbol.sham&&g&&g<41}),eg}function Mg(){return ng?og:(ng=1,og=Pg()&&!Symbol.sham&&"symbol"==typeof Symbol.iterator)}function zg(){if(rg)return sg;rg=1;var g=Dg(),A=D(),t=Ng(),C=Object;return sg=Mg()?function(g){return"symbol"==typeof g}:function(I){var e=g("Symbol");return A(e)&&t(e.prototype,C(I))},sg}function Bg(){if(dg)return ag;dg=1;var g=String;return ag=function(A){try{return g(A)}catch(g){return"Object"}}}function Zg(){if(lg)return hg;lg=1;var g=D(),A=Bg(),t=TypeError;return hg=function(C){if(g(C))return C;throw new t(A(C)+" is not a function")}}function Sg(){if(ug)return cg;ug=1;var g=Zg(),A=wg();return cg=function(t,C){var I=t[C];return A(I)?void 0:g(I)}}function Fg(){if(fg)return pg;fg=1;var g=B(),A=D(),t=Og(),C=TypeError;return pg=function(I,e){var i,o;if("string"===e&&A(i=I.toString)&&!t(o=g(i,I)))return o;if(A(i=I.valueOf)&&!t(o=g(i,I)))return o;if("string"!==e&&A(i=I.toString)&&!t(o=g(i,I)))return o;throw new C("Can't convert object to primitive value")}}var Gg,jg,Lg,Vg,Yg,Wg,Qg,Ug,Kg,Hg,Xg,_g,Jg,qg,$g,gA,AA,tA,CA,IA,eA,iA,oA,nA,sA,rA,aA,dA,hA={exports:{}};function lA(){return jg?Gg:(jg=1,Gg=!0)}function cA(){if(Vg)return Lg;Vg=1;var g=m(),A=Object.defineProperty;return Lg=function(t,C){try{A(g,t,{value:C,configurable:!0,writable:!0})}catch(A){g[t]=C}return C}}function uA(){if(Yg)return hA.exports;Yg=1;var g=lA(),A=m(),t=cA(),C="__core-js_shared__",I=hA.exports=A[C]||t(C,{});return(I.versions||(I.versions=[])).push({version:"3.49.0",mode:g?"pure":"global",copyright:"© 2013–2025 Denis Pushkarev (zloirock.ru), 2025–2026 CoreJS Company (core-js.io). All rights reserved.",license:"https://github.com/zloirock/core-js/blob/v3.49.0/LICENSE",source:"https://github.com/zloirock/core-js"}),hA.exports}function pA(){if(Qg)return Wg;Qg=1;var g=uA();return Wg=function(A,t){return g[A]||(g[A]=t||{})}}function fA(){if(Kg)return Ug;Kg=1;var g=xg(),A=Object;return Ug=function(t){return A(g(t))}}function vA(){if(Xg)return Hg;Xg=1;var g=E(),A=fA(),t=g({}.hasOwnProperty);return Hg=Object.hasOwn||function(g,C){return t(A(g),C)},Hg}function bA(){if(Jg)return _g;Jg=1;var g=E(),A=0,t=Math.random(),C=g(1.1.toString);return _g=function(g){return"Symbol("+(void 0===g?"":g)+")_"+C(++A+t,36)}}function mA(){if($g)return qg;$g=1;var g=m(),A=pA(),t=vA(),C=bA(),I=Pg(),e=Mg(),i=g.Symbol,o=A("wks"),n=e?i.for||i:i&&i.withoutSetter||C;return qg=function(g){return t(o,g)||(o[g]=I&&t(i,g)?i[g]:n("Symbol."+g)),o[g]}}function yA(){if(AA)return gA;AA=1;var g=B(),A=Og(),t=zg(),C=Sg(),I=Fg(),e=TypeError,i=mA()("toPrimitive");return gA=function(o,n){if(!A(o)||t(o))return o;var s,r=C(o,i);if(r){if(void 0===n&&(n="default"),s=g(r,o,n),!A(s)||t(s))return s;throw new e("Can't convert object to primitive value")}return void 0===n&&(n="number"),I(o,n)}}function wA(){if(CA)return tA;CA=1;var g=yA(),A=zg();return tA=function(t){var C=g(t,"string");return A(C)?C:C+""}}function xA(){if(eA)return IA;eA=1;var g=m(),A=Og(),t=g.document,C=A(t)&&A(t.createElement);return IA=function(g){return C?t.createElement(g):{}},IA}function EA(){if(oA)return iA;oA=1;var g=z(),A=y(),t=xA();return iA=!g&&!A(function(){return 7!==Object.defineProperty(t("div"),"a",{get:function(){return 7}}).a})}function OA(){if(nA)return M;nA=1;var g=z(),A=B(),t=bg(),C=mg(),I=Eg(),e=wA(),i=vA(),o=EA(),n=Object.getOwnPropertyDescriptor;return M.f=g?n:function(g,s){if(g=I(g),s=e(s),o)try{return n(g,s)}catch(g){}if(i(g,s))return C(!A(t.f,g,s),g[s])},M}function TA(){if(rA)return sA;rA=1;var g=y(),A=D(),t=/#|\.prototype\./,C=function(t,C){var n=e[I(t)];return n===o||n!==i&&(A(C)?g(C):!!C)},I=C.normalize=function(g){return String(g).replace(t,".").toLowerCase()},e=C.data={},i=C.NATIVE="N",o=C.POLYFILL="P";return sA=C}function DA(){if(dA)return aA;dA=1;var g=T(),A=Zg(),t=w(),C=g(g.bind);return aA=function(g,I){return A(g),void 0===I?g:t?C(g,I):function(){return g.apply(I,arguments)}},aA}var NA,kA,RA,PA,MA,zA,BA,ZA,SA,FA,GA,jA,LA,VA,YA,WA,QA,UA,KA,HA,XA,_A,JA,qA,$A,gt,At,tt,Ct,It={};function et(){return kA?NA:(kA=1,NA=z()&&y()(function(){return 42!==Object.defineProperty(function(){},"prototype",{value:42,writable:!1}).prototype}))}function it(){if(PA)return RA;PA=1;var g=Og(),A=String,t=TypeError;return RA=function(C){if(g(C))return C;throw new t(A(C)+" is not an object")}}function ot(){if(MA)return It;MA=1;var g=z(),A=EA(),t=et(),C=it(),I=wA(),e=TypeError,i=Object.defineProperty,o=Object.getOwnPropertyDescriptor,n="enumerable",s="configurable",r="writable";return It.f=g?t?function(g,A,t){if(C(g),A=I(A),C(t),"function"==typeof g&&"prototype"===A&&"value"in t&&r in t&&!t[r]){var e=o(g,A);e&&e[r]&&(g[A]=t.value,t={configurable:s in t?t[s]:e[s],enumerable:n in t?t[n]:e[n],writable:!1})}return i(g,A,t)}:i:function(g,t,o){if(C(g),t=I(t),C(o),A)try{return i(g,t,o)}catch(g){}if("get"in o||"set"in o)throw new e("Accessors not supported");return"value"in o&&(g[t]=o.value),g},It}function nt(){if(BA)return zA;BA=1;var g=z(),A=ot(),t=mg();return zA=g?function(g,C,I){return A.f(g,C,t(1,I))}:function(g,A,t){return g[A]=t,g},zA}function st(){if(SA)return ZA;SA=1;var g=m(),A=x(),t=T(),C=D(),I=OA().f,e=TA(),i=Tg(),o=DA(),n=nt(),s=vA(),r=function(g){var t=function(C,I,e){if(this instanceof t){switch(arguments.length){case 0:return new g;case 1:return new g(C);case 2:return new g(C,I)}return new g(C,I,e)}return A(g,this,arguments)};return t.prototype=g.prototype,t};return ZA=function(A,a){var d,h,l,c,u,p,f,v,b,m=A.target,y=A.global,w=A.stat,x=A.proto,E=y?g:w?g[m]:g[m]&&g[m].prototype,O=y?i:i[m]||n(i,m,{})[m],T=O.prototype;for(c in a)h=!(d=e(y?c:m+(w?".":"#")+c,A.forced))&&E&&s(E,c),p=O[c],h&&(f=A.dontCallGetSet?(b=I(E,c))&&b.value:E[c]),u=h&&f?f:a[c],(d||x||typeof p!=typeof u)&&(v=A.bind&&h?o(u,g):A.wrap&&h?r(u):x&&C(u)?t(u):u,(A.sham||u&&u.sham||p&&p.sham)&&n(v,"sham",!0),n(O,c,v),x&&(s(i,l=m+"Prototype")||n(i,l,{}),n(i[l],c,u),A.real&&T&&(d||!T[c])&&n(T,c,u)))},ZA}function rt(){if(GA)return FA;GA=1;var g=Math.ceil,A=Math.floor;return FA=Math.trunc||function(t){var C=+t;return(C>0?A:g)(C)}}function at(){if(LA)return jA;LA=1;var g=rt();return jA=function(A){var t=+A;return t!=t||0===t?0:g(t)},jA}function dt(){if(YA)return VA;YA=1;var g=at(),A=Math.max,t=Math.min;return VA=function(C,I){var e=g(C);return e<0?A(e+I,0):t(e,I)},VA}function ht(){if(QA)return WA;QA=1;var g=at(),A=Math.min;return WA=function(t){var C=g(t);return C>0?A(C,9007199254740991):0}}function lt(){if(KA)return UA;KA=1;var g=ht();return UA=function(A){return g(A.length)}}function ct(){if(XA)return HA;XA=1;var g=Eg(),A=dt(),t=lt(),C=function(C){return function(I,e,i){var o=g(I),n=t(o);if(0===n)return!C&&-1;var s,r=A(i,n);if(C&&e!=e){for(;n>r;)if((s=o[r++])!=s)return!0}else for(;n>r;r++)if((C||r in o)&&o[r]===e)return C||r||0;return!C&&-1}};return HA={includes:C(!0),indexOf:C(!1)}}function ut(){return JA?_A:(JA=1,_A={})}function pt(){if($A)return qA;$A=1;var g=E(),A=vA(),t=Eg(),C=ct().indexOf,I=ut(),e=g([].push);return qA=function(g,i){var o,n=t(g),s=0,r=[];for(o in n)!A(I,o)&&A(n,o)&&e(r,o);for(;i.length>s;)A(n,o=i[s++])&&(~C(r,o)||e(r,o));return r},qA}function ft(){return At?gt:(At=1,gt=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"])}function vt(){if(Ct)return tt;Ct=1;var g=pt(),A=ft();return tt=Object.keys||function(t){return g(t,A)}}var bt,mt,yt,wt,xt,Et,Ot,Tt,Dt,Nt,kt={};function Rt(){return bt||(bt=1,kt.f=Object.getOwnPropertySymbols),kt}function Pt(){if(yt)return mt;yt=1;var g=z(),A=E(),t=B(),C=y(),I=vt(),e=Rt(),i=bg(),o=fA(),n=yg(),s=Object.assign,r=Object.defineProperty,a=A([].concat);return mt=!s||C(function(){if(g&&1!==s({b:1},s(r({},"a",{enumerable:!0,get:function(){r(this,"b",{value:3,enumerable:!1})}}),{b:2})).b)return!0;var A={},t={},C=Symbol("assign detection"),e="abcdefghijklmnopqrst";return A[C]=7,e.split("").forEach(function(g){t[g]=g}),7!==s({},A)[C]||I(s({},t)).join("")!==e})?function(A,C){for(var s=o(A),r=arguments.length,d=1,h=e.f,l=i.f;r>d;)for(var c,u=n(arguments[d++]),p=h?a(I(u),h(u)):I(u),f=p.length,v=0;f>v;)c=p[v++],g&&!t(l,u,c)||(s[c]=u[c]);return s}:s,mt}function Mt(){return Et?xt:(Et=1,function(){if(wt)return b;wt=1;var g=st(),A=Pt();g({target:"Object",stat:!0,arity:2,forced:Object.assign!==A},{assign:A})}(),xt=Tg().Object.assign)}function zt(){return Tt?Ot:(Tt=1,Ot=Mt())}var Bt,Zt,St,Ft,Gt,jt,Lt,Vt,Yt,Wt,Qt,Ut,Kt,Ht,Xt,_t=C(Nt?Dt:(Nt=1,Dt=zt())),Jt={};function qt(){return Zt?Bt:(Zt=1,Bt=E()([].slice))}function $t(){if(Ft)return St;Ft=1;var g=E(),A=Zg(),t=Og(),C=vA(),I=qt(),e=w(),i=Function,o=g([].concat),n=g([].join),s={};return St=e?i.bind:function(g){var e=A(this),r=e.prototype,a=I(arguments,1),d=function(){var A=o(a,I(arguments));return this instanceof d?function(g,A,t){if(!C(s,A)){for(var I=[],e=0;e=.1;)l=+e[a++%i],l>r&&(l=r),h=Math.sqrt(l*l/(1+s*s)),h=o<0?-h:h,A+=h,t+=s*h,!0===d?g.lineTo(A,t):g.moveTo(A,t),r-=l,d=!d}const rC={circle:eC,dashedLine:sC,database:nC,diamond:function(g,A,t,C){g.beginPath(),g.lineTo(A,t+C),g.lineTo(A+C,t),g.lineTo(A,t-C),g.lineTo(A-C,t),g.closePath()},ellipse:oC,ellipse_vis:oC,hexagon:function(g,A,t,C){g.beginPath();const I=2*Math.PI/6;g.moveTo(A+C,t);for(let e=1;e<6;e++)g.lineTo(A+C*Math.cos(I*e),t+C*Math.sin(I*e));g.closePath()},roundRect:iC,square:function(g,A,t,C){g.beginPath(),g.rect(A-C,t-C,2*C,2*C),g.closePath()},star:function(g,A,t,C){g.beginPath(),t+=.1*(C*=.82);for(let I=0;I<10;I++){const e=I%2==0?1.3*C:.5*C;g.lineTo(A+e*Math.sin(2*I*Math.PI/10),t-e*Math.cos(2*I*Math.PI/10))}g.closePath()},triangle:function(g,A,t,C){g.beginPath(),t+=.275*(C*=1.15);const I=2*C,e=I/2,i=Math.sqrt(3)/6*I,o=Math.sqrt(I*I-e*e);g.moveTo(A,t-(o-i)),g.lineTo(A+e,t+i),g.lineTo(A-e,t+i),g.lineTo(A,t-(o-i)),g.closePath()},triangleDown:function(g,A,t,C){g.beginPath(),t-=.275*(C*=1.15);const I=2*C,e=I/2,i=Math.sqrt(3)/6*I,o=Math.sqrt(I*I-e*e);g.moveTo(A,t+(o-i)),g.lineTo(A+e,t-i),g.lineTo(A-e,t-i),g.lineTo(A,t+(o-i)),g.closePath()}};var aC,dC={exports:{}};var hC,lC,cC,uC,pC,fC,vC,bC,mC,yC,wC,xC,EC,OC=(aC||(aC=1,function(g){function A(g){if(g)return function(g){for(var t in A.prototype)g[t]=A.prototype[t];return g}(g)}g.exports=A,A.prototype.on=A.prototype.addEventListener=function(g,A){return this._callbacks=this._callbacks||{},(this._callbacks["$"+g]=this._callbacks["$"+g]||[]).push(A),this},A.prototype.once=function(g,A){function t(){this.off(g,t),A.apply(this,arguments)}return t.fn=A,this.on(g,t),this},A.prototype.off=A.prototype.removeListener=A.prototype.removeAllListeners=A.prototype.removeEventListener=function(g,A){if(this._callbacks=this._callbacks||{},0==arguments.length)return this._callbacks={},this;var t,C=this._callbacks["$"+g];if(!C)return this;if(1==arguments.length)return delete this._callbacks["$"+g],this;for(var I=0;I9007199254740991)throw new g("Maximum allowed index exceeded");return A},BC}function II(){if(FC)return SC;FC=1;var g=z(),A=ot(),t=mg();return SC=function(C,I,e){g?A.f(C,I,t(0,e)):C[I]=e},SC}function eI(){if(jC)return GC;jC=1;var g=z(),A=DC(),t=TypeError,C=Object.getOwnPropertyDescriptor,I=g&&!function(){if(void 0!==this)return!0;try{Object.defineProperty([],"length",{writable:!1}).length=1}catch(g){return g instanceof TypeError}}();return GC=I?function(g,I){if(A(g)&&!C(g,"length").writable)throw new t("Cannot set read only .length");return g.length=I}:function(g,A){return g.length=A}}function iI(){if(VC)return LC;VC=1;var g={};return g[mA()("toStringTag")]="z",LC="[object z]"===String(g)}function oI(){if(WC)return YC;WC=1;var g=iI(),A=D(),t=O(),C=mA()("toStringTag"),I=Object,e="Arguments"===t(function(){return arguments}());return YC=g?t:function(g){var i,o,n;return void 0===g?"Undefined":null===g?"Null":"string"==typeof(o=function(g,A){try{return g[A]}catch(g){}}(i=I(g),C))?o:e?t(i):"Object"===(n=t(i))&&A(i.callee)?"Arguments":n},YC}function nI(){if(UC)return QC;UC=1;var g=E(),A=D(),t=uA(),C=g(Function.toString);return A(t.inspectSource)||(t.inspectSource=function(g){return C(g)}),QC=t.inspectSource}function sI(){if(HC)return KC;HC=1;var g=E(),A=y(),t=D(),C=oI(),I=Dg(),e=nI(),i=function(){},o=I("Reflect","construct"),n=/^\s*(?:class|function)\b/,s=g(n.exec),r=!n.test(i),a=function(g){if(!t(g))return!1;try{return o(i,[],g),!0}catch(g){return!1}},d=function(g){if(!t(g))return!1;switch(C(g)){case"AsyncFunction":case"GeneratorFunction":case"AsyncGeneratorFunction":return!1}try{return r||!!s(n,e(g))}catch(g){return!0}};return d.sham=!0,KC=!o||A(function(){var g;return a(a.call)||!a(Object)||!a(function(){g=!0})||g})?d:a}function rI(){if(_C)return XC;_C=1;var g=DC(),A=sI(),t=Og(),C=mA()("species"),I=Array;return XC=function(e){var i;return g(e)&&(i=e.constructor,(A(i)&&(i===I||g(i.prototype))||t(i)&&null===(i=i[C]))&&(i=void 0)),void 0===i?I:i}}function aI(){if(qC)return JC;qC=1;var g=rI();return JC=function(A,t){return new(g(A))(0===t?0:t)}}function dI(){if(gI)return $C;gI=1;var g=y(),A=mA(),t=Rg(),C=A("species");return $C=function(A){return t>=51||!g(function(){var g=[];return(g.constructor={})[C]=function(){return{foo:1}},1!==g[A](Boolean).foo})},$C}function hI(){if(AI)return tI;AI=1;var g=st(),A=y(),t=DC(),C=Og(),I=fA(),e=lt(),i=CI(),o=II(),n=eI(),s=aI(),r=dI(),a=mA(),d=Rg(),h=a("isConcatSpreadable"),l=d>=51||!A(function(){var g=[];return g[h]=!1,g.concat()[0]!==g}),c=function(g){if(!C(g))return!1;var A=g[h];return void 0!==A?!!A:t(g)};return g({target:"Array",proto:!0,arity:1,forced:!l||!r("concat")},{concat:function(g){var A,t,C,r,a,d=I(this),h=s(d,0),l=0;for(A=-1,C=arguments.length;Ar;)t.f(g,i=n[r++],o[i]);return g},OI}function DI(){return mI?bI:(mI=1,bI=Dg()("document","documentElement"))}function NI(){if(wI)return yI;wI=1;var g=pA(),A=bA(),t=g("keys");return yI=function(g){return t[g]||(t[g]=A(g))}}function kI(){if(EI)return xI;EI=1;var g,A=it(),t=TI(),C=ft(),I=ut(),e=DI(),i=xA(),o="prototype",n="script",s=NI()("IE_PROTO"),r=function(){},a=function(g){return"<"+n+">"+g+""},d=function(g){g.write(a("")),g.close();var A=g.parentWindow.Object;return g=null,A},h=function(){try{g=new ActiveXObject("htmlfile")}catch(g){}var A,t,I;h="undefined"!=typeof document?document.domain&&g?d(g):(t=i("iframe"),I="java"+n+":",t.style.display="none",e.appendChild(t),t.src=String(I),(A=t.contentWindow.document).open(),A.write(a("document.F=Object")),A.close(),A.F):d(g);for(var s=C.length;s--;)delete h[o][C[s]];return h()};return I[s]=!0,xI=Object.create||function(g,C){var I;return null!==g?(r[o]=A(g),I=new r,r[o]=null,I[s]=g):I=h(),void 0===C?I:t.f(I,C)}}var RI,PI={};function MI(){if(RI)return PI;RI=1;var g=pt(),A=ft().concat("length","prototype");return PI.f=Object.getOwnPropertyNames||function(t){return g(t,A)},PI}var zI,BI,ZI,SI,FI,GI={};function jI(){if(zI)return GI;zI=1;var g=O(),A=Eg(),t=MI().f,C=qt(),I="object"==typeof window&&window&&Object.getOwnPropertyNames?Object.getOwnPropertyNames(window):[];return GI.f=function(e){return I&&"Window"===g(e)?function(g){try{return t(g)}catch(g){return C(I)}}(e):t(A(e))},GI}function LI(){if(ZI)return BI;ZI=1;var g=nt();return BI=function(A,t,C,I){return I&&I.enumerable?A[t]=C:g(A,t,C),A},BI}function VI(){if(FI)return SI;FI=1;var g=ot();return SI=function(A,t,C){return g.f(A,t,C)}}var YI,WI,QI,UI,KI,HI,XI,_I,JI,qI,$I,ge,Ae,te,Ce,Ie,ee={};function ie(){if(YI)return ee;YI=1;var g=mA();return ee.f=g,ee}function oe(){if(QI)return WI;QI=1;var g=Tg(),A=vA(),t=ie(),C=ot().f;return WI=function(I){var e=g.Symbol||(g.Symbol={});A(e,I)||C(e,I,{value:t.f(I)})}}function ne(){if(KI)return UI;KI=1;var g=B(),A=Dg(),t=mA(),C=LI();return UI=function(){var I=A("Symbol"),e=I&&I.prototype,i=e&&e.valueOf,o=t("toPrimitive");e&&!e[o]&&C(e,o,function(A){return g(i,this)},{arity:1})}}function se(){if(XI)return HI;XI=1;var g=iI(),A=oI();return HI=g?{}.toString:function(){return"[object "+A(this)+"]"}}function re(){if(JI)return _I;JI=1;var g=iI(),A=ot().f,t=nt(),C=vA(),I=se(),e=mA()("toStringTag");return _I=function(i,o,n,s){var r=n?i:i&&i.prototype;r&&(C(r,e)||A(r,e,{configurable:!0,value:o}),s&&!g&&t(r,"toString",I))},_I}function ae(){if($I)return qI;$I=1;var g=m(),A=D(),t=g.WeakMap;return qI=A(t)&&/native code/.test(String(t))}function de(){if(Ae)return ge;Ae=1;var g,A,t,C=ae(),I=m(),e=Og(),i=nt(),o=vA(),n=uA(),s=NI(),r=ut(),a="Object already initialized",d=I.TypeError,h=I.WeakMap;if(C||n.state){var l=n.state||(n.state=new h);l.get=l.get,l.has=l.has,l.set=l.set,g=function(g,A){if(l.has(g))throw new d(a);return A.facade=g,l.set(g,A),A},A=function(g){return l.get(g)||{}},t=function(g){return l.has(g)}}else{var c=s("state");r[c]=!0,g=function(g,A){if(o(g,c))throw new d(a);return A.facade=g,i(g,c,A),A},A=function(g){return o(g,c)?g[c]:{}},t=function(g){return o(g,c)}}return ge={set:g,get:A,has:t,enforce:function(C){return t(C)?A(C):g(C,{})},getterFor:function(g){return function(t){var C;if(!e(t)||(C=A(t)).type!==g)throw new d("Incompatible receiver, "+g+" required");return C}}},ge}function he(){if(Ce)return te;Ce=1;var g=DA(),A=yg(),t=fA(),C=lt(),I=aI(),e=II(),i=function(i){var o=1===i,n=2===i,s=3===i,r=4===i,a=6===i,d=7===i,h=5===i||a;return function(l,c,u){for(var p,f,v=t(l),b=A(v),m=C(b),y=g(c,u),w=0,x=0,E=o?I(l,m):n||d?I(l,0):void 0;m>w;w++)if((h||w in b)&&(f=y(p=b[w],w,v),i))if(o)e(E,w,f);else if(f)switch(i){case 3:return!0;case 5:return p;case 6:return w;case 2:e(E,x++,p)}else switch(i){case 4:return!1;case 7:e(E,x++,p)}return a?-1:s||r?r:E}};return te={forEach:i(0),map:i(1),filter:i(2),some:i(3),every:i(4),find:i(5),findIndex:i(6),filterReject:i(7)}}var le,ce,ue,pe={};function fe(){return ce?le:(ce=1,le=Pg()&&!!Symbol.for&&!!Symbol.keyFor)}var ve,be={};var me,ye,we,xe,Ee,Oe,Te,De={};function Ne(){if(ye)return me;ye=1;var g=Og(),A=de().get;return me=function(t){if(!g(t))return!1;var C=A(t);return!!C&&"RawJSON"===C.type}}function ke(){if(xe)return we;xe=1;var g=E(),A=vA(),t=SyntaxError,C=parseInt,I=String.fromCharCode,e=g("".charAt),i=g("".slice),o=g(/./.exec),n={'\\"':'"',"\\\\":"\\","\\/":"/","\\b":"\b","\\f":"\f","\\n":"\n","\\r":"\r","\\t":"\t"},s=/^[\da-f]{4}$/i,r=/^[\u0000-\u001F]$/;return we=function(g,a){for(var d=!0,h="";a=A.length)return g.target=null,i(void 0,!0);switch(g.kind){case"keys":return i(t,!1);case"values":return i(A[t],!1)}return i([t,A[t]],!1)},"values");var d=t.Arguments=t.Array;if(A("keys"),A("values"),A("entries"),!o&&n&&"values"!==d.name)try{I(d,"name",{value:"values"})}catch(g){}return Fi}function io(){return Li?ji:(Li=1,ji={CSSRuleList:0,CSSStyleDeclaration:0,CSSValueList:0,ClientRectList:0,DOMRectList:0,DOMStringList:0,DOMTokenList:1,DataTransferItemList:0,FileList:0,HTMLAllCollection:0,HTMLCollection:0,HTMLFormElement:0,HTMLSelectElement:0,MediaList:0,MimeTypeArray:0,NamedNodeMap:0,NodeList:1,PaintRequestList:0,Plugin:0,PluginArray:0,SVGLengthList:0,SVGNumberList:0,SVGPathSegList:0,SVGPointList:0,SVGStringList:0,SVGTransformList:0,SourceBufferList:0,StyleSheetList:0,TextTrackCueList:0,TextTrackList:0,TouchList:0})}function oo(){if(Vi)return Qi;Vi=1,eo();var g=io(),A=m(),t=re(),C=Ki();for(var I in g)t(A[I],I),C[I]=C.Array;return Qi}function no(){if(Wi)return Yi;Wi=1;var g=ai();return oo(),Yi=g}var so,ro={};var ao;var ho;var lo,co,uo;function po(){if(uo)return co;uo=1;var g=no();return function(){if(so)return ro;so=1;var g=mA(),A=ot().f,t=g("metadata"),C=Function.prototype;void 0===C[t]&&A(C,t,{value:null})}(),ao||(ao=1,Ge()),ho||(ho=1,Ye()),lo||(lo=1,oe()("metadata")),co=g}var fo,vo,bo;function mo(){if(vo)return fo;vo=1;var g=Dg(),A=E(),t=g("Symbol"),C=t.keyFor,I=A(t.prototype.valueOf);return fo=t.isRegisteredSymbol||function(g){try{return void 0!==C(I(g))}catch(g){return!1}}}var yo,wo,xo;function Eo(){if(wo)return yo;wo=1;for(var g=pA(),A=Dg(),t=E(),C=zg(),I=mA(),e=A("Symbol"),i=e.isWellKnownSymbol,o=A("Object","getOwnPropertyNames"),n=t(e.prototype.valueOf),s=g("wks"),r=0,a=o(e),d=a.length;r=h?g?"":void 0:(s=e(a,d))<55296||s>56319||d+1===h||(r=e(a,d+1))<56320||r>57343?g?I(a,d):s:g?i(a,d,d+2):r-56320+(s-55296<<10)+65536}};return jo={codeAt:o(!1),charAt:o(!0)}}function In(){if(Vo)return tn;Vo=1;var g=Cn().charAt,A=fI(),t=de(),C=Co(),I=Io(),e="String Iterator",i=t.set,o=t.getterFor(e);return C(String,"String",function(g){i(this,{type:e,string:A(g),index:0})},function(){var A,t=o(this),C=t.string,e=t.index;return e>=C.length?I(void 0,!0):(A=g(C,e),t.index+=A.length,I(A,!1))}),tn}function en(){if(Wo)return Yo;Wo=1;var g=oI(),A=Sg(),t=wg(),C=Ki(),I=mA()("iterator");return Yo=function(e){if(!t(e))return A(e,I)||A(e,"@@iterator")||C[g(e)]},Yo}function on(){return Uo?Qo:(Uo=1,eo(),In(),Qo=en())}function nn(){if(Ho)return Ko;Ho=1;var g=on();return oo(),Ko=g}function sn(){return _o?Xo:(_o=1,Xo=nn())}function rn(){return qo?Jo:(qo=1,Jo=sn())}function an(){return gn?$o:(gn=1,$o=rn())}var dn,hn,ln,cn,un,pn,fn,vn,bn,mn,yn,wn,xn,En=C(an()),On={};function Tn(){return ln?hn:(ln=1,function(){if(dn)return On;dn=1;var g=st(),A=fA(),t=lt(),C=eI(),I=CI();g({target:"Array",proto:!0,arity:1,forced:y()(function(){return 4294967297!==[].push.call({length:4294967296},1)})||!function(){try{Object.defineProperty([],"length",{writable:!1}).push()}catch(g){return g instanceof TypeError}}()},{push:function(g){var e=A(this),i=t(e),o=arguments.length;I(i+o);for(var n=0;n1?arguments[1]:void 0,p=void 0!==u;p&&(u=g(u,c>2?arguments[2]:void 0));var f,v,b,m,y,w,x=t(h),E=r(x),O=0;if(!E||this===d&&I(E))for(f=i(x),v=l?new this(f):d(f);f>O;O++)w=p?u(x[O],O):x[O],o(v,O,w);else for(v=l?new this:[],y=(m=s(x,E)).next;!(b=A(y,m)).done;O++){w=p?C(m,u,[b.value,O],!0):b.value;try{o(v,O,w)}catch(g){a(m,"throw",g)}}return n(v,O),v},ns}function ks(){if(as)return rs;as=1;var g=mA()("iterator"),A=!1;try{var t=0,C={next:function(){return{done:!!t++}},return:function(){A=!0}};C[g]=function(){return this},Array.from(C,function(){throw 2})}catch(g){}return rs=function(t,C){try{if(!C&&!A)return!1}catch(g){return!1}var I=!1;try{var e={};e[g]=function(){return{next:function(){return{done:I=!0}}}},t(e)}catch(g){}return I},rs}function Rs(){return ls?hs:(ls=1,In(),function(){if(ds)return xs;ds=1;var g=st(),A=Ns();g({target:"Array",stat:!0,forced:!ks()(function(g){Array.from(g)})},{from:A})}(),hs=Tg().Array.from)}function Ps(){return us?cs:(us=1,cs=Rs())}function Ms(){return fs?ps:(fs=1,ps=Ps())}function zs(){return bs?vs:(bs=1,vs=Ms())}function Bs(){return ys?ms:(ys=1,ms=zs())}var Zs,Ss,Fs=C(Bs());function Gs(g,A){(null==A||A>g.length)&&(A=g.length);for(var t=0,C=Array(A);t1?arguments[1]:void 0)}})}(),or=gC()("Array","map"))}function fr(){if(rr)return sr;rr=1;var g=Ng(),A=pr(),t=Array.prototype;return sr=function(C){var I=C.map;return C===t||g(t,C)&&I===t.map?A:I},sr}function vr(){return dr?ar:(dr=1,ar=fr())}var br,mr,yr,wr,xr,Er,Or,Tr=C(lr?hr:(lr=1,hr=vr())),Dr={};function Nr(){return yr?mr:(yr=1,function(){if(br)return Dr;br=1;var g=st(),A=fA(),t=vt();g({target:"Object",stat:!0,forced:y()(function(){t(1)})},{keys:function(g){return t(A(g))}})}(),mr=Tg().Object.keys)}function kr(){return xr?wr:(xr=1,wr=Nr())}var Rr,Pr,Mr,zr,Br,Zr,Sr,Fr=C(Or?Er:(Or=1,Er=kr())),Gr={};function jr(){return Mr?Pr:(Mr=1,function(){if(Rr)return Gr;Rr=1;var g=st(),A=Date,t=E()(A.prototype.getTime);g({target:"Date",stat:!0},{now:function(){return t(new A)}})}(),Pr=Tg().Date.now)}function Lr(){return Br?zr:(Br=1,zr=jr())}var Vr,Yr,Wr,Qr,Ur,Kr,Hr,Xr,_r,Jr,qr,$r,ga,Aa=C(Sr?Zr:(Sr=1,Zr=Lr())),ta={};function Ca(){if(Yr)return Vr;Yr=1;var g=y();return Vr=function(A,t){var C=[][A];return!!C&&g(function(){C.call(null,t||function(){return 1},1)})}}function Ia(){if(Qr)return Wr;Qr=1;var g=he().forEach,A=Ca()("forEach");return Wr=A?[].forEach:function(A){return g(this,A,arguments.length>1?arguments[1]:void 0)},Wr}function ea(){return Hr?Kr:(Hr=1,function(){if(Ur)return ta;Ur=1;var g=st(),A=Ia();g({target:"Array",proto:!0,forced:[].forEach!==A},{forEach:A})}(),Kr=gC()("Array","forEach"))}function ia(){return _r?Xr:(_r=1,Xr=ea())}function oa(){if(qr)return Jr;qr=1;var g=oI(),A=vA(),t=Ng(),C=ia(),I=Array.prototype,e={DOMTokenList:!0,NodeList:!0};return Jr=function(i){var o=i.forEach;return i===I||t(I,i)&&o===I.forEach||A(e,g(i))?C:o},Jr}var na,sa,ra,aa,da,ha,la,ca,ua,pa=C(ga?$r:(ga=1,$r=oa())),fa={};function va(){return ra?sa:(ra=1,function(){if(na)return fa;na=1;var g=st(),A=E(),t=DC(),C=A([].reverse),I=[1,2];g({target:"Array",proto:!0,forced:String(I)===String(I.reverse())},{reverse:function(){return t(this)&&(this.length=this.length),C(this)}})}(),sa=gC()("Array","reverse"))}function ba(){if(da)return aa;da=1;var g=Ng(),A=va(),t=Array.prototype;return aa=function(C){var I=C.reverse;return C===t||g(t,C)&&I===t.reverse?A:I},aa}function ma(){return la?ha:(la=1,ha=ba())}var ya,wa,xa,Ea,Oa,Ta,Da,Na,ka,Ra,Pa,Ma=C(ua?ca:(ua=1,ca=ma())),za={};function Ba(){if(wa)return ya;wa=1;var g=Bg(),A=TypeError;return ya=function(t,C){if(!delete t[C])throw new A("Cannot delete property "+g(C)+" of "+g(t))}}function Za(){return Oa?Ea:(Oa=1,function(){if(xa)return za;xa=1;var g=st(),A=fA(),t=dt(),C=at(),I=lt(),e=eI(),i=CI(),o=aI(),n=II(),s=Ba(),r=dI()("splice"),a=Math.max,d=Math.min;g({target:"Array",proto:!0,forced:!r},{splice:function(g,r){var h,l,c,u,p,f,v=A(this),b=I(v),m=t(g,b),y=arguments.length;for(0===y?h=l=0:1===y?(h=0,l=b-m):(h=y-2,l=d(a(C(r),0),b-m)),i(b+h-l),c=o(v,l),u=0;ub-l+h;u--)s(v,u-1)}else if(h>l)for(u=b-l;u>m;u--)f=u+h-1,(p=u+l-1)in v?v[f]=v[p]:s(v,f);for(u=0;u1?arguments[1]:void 0)}}),C("includes")}(),ja=gC()("Array","includes"))}var Qa,Ua,Ka,Ha,Xa,_a,Ja,qa,$a,gd,Ad,td,Cd,Id,ed,id={};function od(){if(Ua)return Qa;Ua=1;var g=Og(),A=O(),t=mA()("match");return Qa=function(C){var I;return g(C)&&(void 0!==(I=C[t])?!!I:"RegExp"===A(C))},Qa}function nd(){if(Ha)return Ka;Ha=1;var g=od(),A=TypeError;return Ka=function(t){if(g(t))throw new A("The method doesn't accept regular expressions");return t},Ka}function sd(){if(_a)return Xa;_a=1;var g=mA()("match");return Xa=function(A){var t=/./;try{"/./"[A](t)}catch(C){try{return t[g]=!1,"/./"[A](t)}catch(g){}}return!1}}function rd(){return $a?qa:($a=1,function(){if(Ja)return id;Ja=1;var g=st(),A=E(),t=nd(),C=xg(),I=fI(),e=sd(),i=A("".indexOf);g({target:"String",proto:!0,forced:!e("includes")},{includes:function(g){return!!~i(I(C(this)),I(t(g)),arguments.length>1?arguments[1]:void 0)}})}(),qa=gC()("String","includes"))}function ad(){if(Ad)return gd;Ad=1;var g=Ng(),A=Wa(),t=rd(),C=Array.prototype,I=String.prototype;return gd=function(e){var i=e.includes;return e===C||g(C,e)&&i===C.includes?A:"string"==typeof e||e===I||g(I,e)&&i===I.includes?t:i},gd}function dd(){return Cd?td:(Cd=1,td=ad())}var hd,ld,cd,ud,pd,fd,vd,bd=C(ed?Id:(ed=1,Id=dd())),md={};function yd(){return cd?ld:(cd=1,function(){if(hd)return md;hd=1;var g=st(),A=y(),t=fA(),C=_i(),I=Xi();g({target:"Object",stat:!0,forced:A(function(){C(1)}),sham:!I},{getPrototypeOf:function(g){return C(t(g))}})}(),ld=Tg().Object.getPrototypeOf)}function wd(){return pd?ud:(pd=1,ud=yd())}var xd,Ed,Od,Td,Dd,Nd,kd,Rd,Pd=C(vd?fd:(vd=1,fd=wd()));function Md(){return Ed?xd:(Ed=1,hI(),xd=gC()("Array","concat"))}function zd(){if(Td)return Od;Td=1;var g=Ng(),A=Md(),t=Array.prototype;return Od=function(C){var I=C.concat;return C===t||g(t,C)&&I===t.concat?A:I},Od}function Bd(){return Nd?Dd:(Nd=1,Dd=zd())}var Zd,Sd,Fd,Gd,jd,Ld,Vd,Yd,Wd,Qd=C(Rd?kd:(Rd=1,kd=Bd())),Ud={};function Kd(){return Fd?Sd:(Fd=1,function(){if(Zd)return Ud;Zd=1;var g=st(),A=he().filter;g({target:"Array",proto:!0,forced:!dI()("filter")},{filter:function(g){return A(this,g,arguments.length>1?arguments[1]:void 0)}})}(),Sd=gC()("Array","filter"))}function Hd(){if(jd)return Gd;jd=1;var g=Ng(),A=Kd(),t=Array.prototype;return Gd=function(C){var I=C.filter;return C===t||g(t,C)&&I===t.filter?A:I},Gd}function Xd(){return Vd?Ld:(Vd=1,Ld=Hd())}var _d,Jd,qd,$d,gh,Ah,th,Ch,Ih,eh=C(Wd?Yd:(Wd=1,Yd=Xd())),ih={};function oh(){if(Jd)return _d;Jd=1;var g=z(),A=y(),t=E(),C=_i(),I=vt(),e=Eg(),i=t(bg().f),o=t([].push),n=g&&A(function(){var g=Object.create(null);return g[2]=2,!i(g,2)}),s=function(A){return function(t){for(var s,r=e(t),a=I(r),d=n&&null===C(r),h=a.length,l=0,c=[];h>l;)s=a[l++],g&&!(d?s in r:i(r,s))||o(c,A?[s,r[s]]:r[s]);return c}};return _d={entries:s(!0),values:s(!1)}}function nh(){return gh?$d:(gh=1,function(){if(qd)return ih;qd=1;var g=st(),A=oh().values;g({target:"Object",stat:!0},{values:function(g){return A(g)}})}(),$d=Tg().Object.values)}function sh(){return th?Ah:(th=1,Ah=nh())}var rh,ah,dh,hh,lh,ch,uh,ph,fh,vh,bh,mh,yh,wh=C(Ih?Ch:(Ih=1,Ch=sh())),xh={};function Eh(){return ah?rh:(ah=1,rh="\t\n\v\f\r                 \u2028\u2029\ufeff")}function Oh(){if(hh)return dh;hh=1;var g=E(),A=xg(),t=fI(),C=Eh(),I=g("".replace),e=RegExp("^["+C+"]+"),i=RegExp("(^|[^"+C+"])["+C+"]+$"),o=function(g){return function(C){var o=t(A(C));return 1&g&&(o=I(o,e,"")),2&g&&(o=I(o,i,"$1")),o}};return dh={start:o(1),end:o(2),trim:o(3)}}function Th(){if(ch)return lh;ch=1;var g=m(),A=y(),t=E(),C=fI(),I=Oh().trim,e=Eh(),i=g.parseInt,o=g.Symbol,n=o&&o.iterator,s=/^[+-]?0x/i,r=t(s.exec),a=8!==i(e+"08")||22!==i(e+"0x16")||n&&!A(function(){i(Object(n))});return lh=a?function(g,A){var t=I(C(g));return i(t,A>>>0||(r(s,t)?16:10))}:i,lh}function Dh(){return fh?ph:(fh=1,function(){if(uh)return xh;uh=1;var g=st(),A=Th();g({global:!0,forced:parseInt!==A},{parseInt:A})}(),ph=Tg().parseInt)}function Nh(){return bh?vh:(bh=1,vh=Dh())}var kh,Rh,Ph,Mh,zh,Bh,Zh,Sh,Fh,Gh=C(yh?mh:(yh=1,mh=Nh())),jh={};function Lh(){return Ph?Rh:(Ph=1,function(){if(kh)return jh;kh=1;var g=st(),A=T(),t=ct().indexOf,C=Ca(),I=A([].indexOf),e=!!I&&1/I([1],1,-0)<0;g({target:"Array",proto:!0,forced:e||!C("indexOf")},{indexOf:function(g){var A=arguments.length>1?arguments[1]:void 0;return e?I(this,g,A)||0:t(this,g,A)}})}(),Rh=gC()("Array","indexOf"))}function Vh(){if(zh)return Mh;zh=1;var g=Ng(),A=Lh(),t=Array.prototype;return Mh=function(C){var I=C.indexOf;return C===t||g(t,C)&&I===t.indexOf?A:I},Mh}function Yh(){return Zh?Bh:(Zh=1,Bh=Vh())}var Wh,Qh,Uh,Kh,Hh,Xh,_h,Jh=C(Fh?Sh:(Fh=1,Sh=Yh()));function qh(){if(Uh)return Qh;Uh=1,Wh||(Wh=1,st()({target:"Object",stat:!0,sham:!z()},{create:kI()}));var g=Tg().Object;return Qh=function(A,t){return g.create(A,t)}}function $h(){return Hh?Kh:(Hh=1,Kh=qh())}var gl,Al,tl,Cl,Il,el,il,ol,nl,sl,rl,al,dl,hl=C(_h?Xh:(_h=1,Xh=$h())),ll={};function cl(){if(Al)return gl;Al=1;var g=at(),A=fI(),t=xg(),C=RangeError,I=Math.floor;return gl=function(e){var i=A(t(this)),o="",n=g(e);if(n<0||n===1/0)throw new C("Wrong number of repetitions");for(;n>0;(n=I(n/2))&&(i+=i))n%2&&(o+=i);return o}}function ul(){if(Cl)return tl;Cl=1;var g=E(),A=ht(),t=fI(),C=cl(),I=xg(),e=g(C),i=g("".slice),o=Math.ceil,n=function(g){return function(C,n,s){var r=t(I(C)),a=A(n),d=r.length;if(a<=d)return r;var h,l,c=void 0===s?" ":t(s);return""===c?r:((l=e(c,o((h=a-d)/c.length))).length>h&&(l=i(l,0,h)),g?r+l:l+r)}};return tl={start:n(!1),end:n(!0)}}function pl(){if(el)return Il;el=1;var g=E(),A=y(),t=ul().start,C=RangeError,I=isFinite,e=Math.abs,i=Date.prototype,o=i.toISOString,n=g(i.getTime),s=g(i.getUTCDate),r=g(i.getUTCFullYear),a=g(i.getUTCHours),d=g(i.getUTCMilliseconds),h=g(i.getUTCMinutes),l=g(i.getUTCMonth),c=g(i.getUTCSeconds);return Il=A(function(){return"0385-07-25T07:06:39.999Z"!==o.call(new Date(-50000000000001))})||!A(function(){o.call(new Date(NaN))})?function(){if(!I(n(this)))throw new C("Invalid time value");var g=this,A=r(g),i=d(g),o=A<0?"-":A>9999?"+":"";return o+t(e(A),o?6:4,0)+"-"+t(l(g)+1,2,0)+"-"+t(s(g),2,0)+"T"+t(a(g),2,0)+":"+t(h(g),2,0)+":"+t(c(g),2,0)+"."+t(i,3,0)+"Z"}:o}function fl(){if(nl)return ol;nl=1,function(){if(il)return ll;il=1;var g=st(),A=B(),t=fA(),C=yA(),I=pl(),e=O();g({target:"Date",proto:!0,forced:y()(function(){return null!==new Date(NaN).toJSON()||1!==A(Date.prototype.toJSON,{toISOString:function(){return 1}})})},{toJSON:function(g){var i=t(this),o=C(i,"number");return"number"!=typeof o||isFinite(o)?"toISOString"in i||"Date"!==e(i)?i.toISOString():A(I,i):null}})}(),Pe();var g=Tg(),A=x();return g.JSON||(g.JSON={stringify:JSON.stringify}),ol=function(t,C,I){return A(g.JSON.stringify,null,arguments)},ol}function vl(){return rl?sl:(rl=1,sl=fl())}var bl,ml,yl,wl,xl,El,Ol,Tl=C(dl?al:(dl=1,al=vl())),Dl={},Nl={};function kl(){if(ml)return bl;ml=1;var g=m(),A=kg(),t=O(),C=function(g){return A.slice(0,g.length)===g};return bl=C("Bun/")?"BUN":C("Cloudflare-Workers")?"CLOUDFLARE":C("Deno/")?"DENO":C("Node.js/")?"NODE":g.Bun&&"string"==typeof Bun.version?"BUN":g.Deno&&"object"==typeof Deno.version?"DENO":"process"===t(g.process)?"NODE":g.window&&g.document?"BROWSER":"REST"}function Rl(){if(wl)return yl;wl=1;var g=TypeError;return yl=function(A,t){if(AI,a=C(e)?e:n(e),d=r?i(arguments,I):[],h=r?function(){t(a,this,d)}:a;return A?g(h,s):g(h)}:g},xl}var Ml,zl,Bl,Zl,Sl,Fl,Gl={};function jl(){return zl||(zl=1,function(){if(Ol)return Nl;Ol=1;var g=st(),A=m(),t=Pl()(A.setInterval,!0);g({global:!0,bind:!0,forced:A.setInterval!==t},{setInterval:t})}(),function(){if(Ml)return Gl;Ml=1;var g=st(),A=m(),t=Pl()(A.setTimeout,!0);g({global:!0,bind:!0,forced:A.setTimeout!==t},{setTimeout:t})}()),Dl}function Ll(){return Zl?Bl:(Zl=1,jl(),Bl=Tg().setTimeout)}var Vl,Yl,Wl,Ql,Ul,Kl,Hl,Xl,_l,Jl,ql,$l=C(Fl?Sl:(Fl=1,Sl=Ll())),gc={};function Ac(){if(Yl)return Vl;Yl=1;var g=fA(),A=dt(),t=lt();return Vl=function(C){for(var I=g(this),e=t(I),i=arguments.length,o=A(i>1?arguments[1]:void 0,e),n=i>2?arguments[2]:void 0,s=void 0===n?e:A(n,e);s>o;)I[o++]=C;return I},Vl}function tc(){return Ul?Ql:(Ul=1,function(){if(Wl)return gc;Wl=1;var g=st(),A=Ac(),t=Ui();g({target:"Array",proto:!0},{fill:A}),t("fill")}(),Ql=gC()("Array","fill"))}function Cc(){if(Hl)return Kl;Hl=1;var g=Ng(),A=tc(),t=Array.prototype;return Kl=function(C){var I=C.fill;return C===t||g(t,C)&&I===t.fill?A:I},Kl}function Ic(){return _l?Xl:(_l=1,Xl=Cc())}var ec,ic=C(ql?Jl:(ql=1,Jl=Ic())); +/*! Hammer.JS - v2.0.17-rc - 2019-12-16 + * http://naver.github.io/egjs + * + * Forked By Naver egjs + * Copyright (c) hammerjs + * Licensed under the MIT license */ +function oc(){return oc=Object.assign||function(g){for(var A=1;A-1}var Gc=function(){function g(g,A){this.manager=g,this.set(A)}var A=g.prototype;return A.set=function(g){g===bc&&(g=this.compute()),vc&&this.manager.element.style&&Oc[g]&&(this.manager.element.style[fc]=g),this.actions=g.toLowerCase().trim()},A.update=function(){this.set(this.manager.options.touchAction)},A.compute=function(){var g=[];return Zc(this.manager.recognizers,function(A){Sc(A.options.enable,[A])&&(g=g.concat(A.getTouchAction()))}),function(g){if(Fc(g,wc))return wc;var A=Fc(g,xc),t=Fc(g,Ec);return A&&t?wc:A||t?A?xc:Ec:Fc(g,yc)?yc:mc}(g.join(" "))},A.preventDefaults=function(g){var A=g.srcEvent,t=g.offsetDirection;if(this.manager.session.prevented)A.preventDefault();else{var C=this.actions,I=Fc(C,wc)&&!Oc[wc],e=Fc(C,Ec)&&!Oc[Ec],i=Fc(C,xc)&&!Oc[xc];if(I){var o=1===g.pointers.length,n=g.distance<2,s=g.deltaTime<250;if(o&&n&&s)return}if(!i||!e)return I||e&&6&t||i&&t&Mc?this.preventSrc(A):void 0}},A.preventSrc=function(g){this.manager.session.prevented=!0,g.preventDefault()},g}();function jc(g,A){for(;g;){if(g===A)return!0;g=g.parentNode}return!1}function Lc(g){var A=g.length;if(1===A)return{x:lc(g[0].clientX),y:lc(g[0].clientY)};for(var t=0,C=0,I=0;I=cc(A)?g<0?2:4:A<0?8:Pc}function Uc(g,A,t){return{x:A/g||0,y:t/g||0}}function Kc(g,A){var t=g.session,C=A.pointers,I=C.length;t.firstInput||(t.firstInput=Vc(A)),I>1&&!t.firstMultiple?t.firstMultiple=Vc(A):1===I&&(t.firstMultiple=!1);var e=t.firstInput,i=t.firstMultiple,o=i?i.center:e.center,n=A.center=Lc(C);A.timeStamp=uc(),A.deltaTime=A.timeStamp-e.timeStamp,A.angle=Wc(o,n),A.distance=Yc(o,n),function(g,A){var t=A.center,C=g.offsetDelta||{},I=g.prevDelta||{},e=g.prevInput||{};1!==A.eventType&&4!==e.eventType||(I=g.prevDelta={x:e.deltaX||0,y:e.deltaY||0},C=g.offsetDelta={x:t.x,y:t.y}),A.deltaX=I.x+(t.x-C.x),A.deltaY=I.y+(t.y-C.y)}(t,A),A.offsetDirection=Qc(A.deltaX,A.deltaY);var s,r,a=Uc(A.deltaTime,A.deltaX,A.deltaY);A.overallVelocityX=a.x,A.overallVelocityY=a.y,A.overallVelocity=cc(a.x)>cc(a.y)?a.x:a.y,A.scale=i?(s=i.pointers,Yc((r=C)[0],r[1],Bc)/Yc(s[0],s[1],Bc)):1,A.rotation=i?function(g,A){return Wc(A[1],A[0],Bc)+Wc(g[1],g[0],Bc)}(i.pointers,C):0,A.maxPointers=t.prevInput?A.pointers.length>t.prevInput.maxPointers?A.pointers.length:t.prevInput.maxPointers:A.pointers.length,function(g,A){var t,C,I,e,i=g.lastInterval||A,o=A.timeStamp-i.timeStamp;if(8!==A.eventType&&(o>25||void 0===i.velocity)){var n=A.deltaX-i.deltaX,s=A.deltaY-i.deltaY,r=Uc(o,n,s);C=r.x,I=r.y,t=cc(r.x)>cc(r.y)?r.x:r.y,e=Qc(n,s),g.lastInterval=A}else t=i.velocity,C=i.velocityX,I=i.velocityY,e=i.direction;A.velocity=t,A.velocityX=C,A.velocityY=I,A.direction=e}(t,A);var d,h=g.element,l=A.srcEvent;jc(d=l.composedPath?l.composedPath()[0]:l.path?l.path[0]:l.target,h)&&(h=d),A.target=h}function Hc(g,A,t){var C=t.pointers.length,I=t.changedPointers.length,e=1&A&&C-I===0,i=12&A&&C-I===0;t.isFirst=!!e,t.isFinal=!!i,e&&(g.session={}),t.eventType=A,Kc(g,t),g.emit("hammer.input",t),g.recognize(t),g.session.prevInput=t}function Xc(g){return g.trim().split(/\s+/g)}function _c(g,A,t){Zc(Xc(A),function(A){g.addEventListener(A,t,!1)})}function Jc(g,A,t){Zc(Xc(A),function(A){g.removeEventListener(A,t,!1)})}function qc(g){var A=g.ownerDocument||g;return A.defaultView||A.parentWindow||window}var $c=function(){function g(g,A){var t=this;this.manager=g,this.callback=A,this.element=g.element,this.target=g.options.inputTarget,this.domHandler=function(A){Sc(g.options.enable,[g])&&t.handler(A)},this.init()}var A=g.prototype;return A.handler=function(){},A.init=function(){this.evEl&&_c(this.element,this.evEl,this.domHandler),this.evTarget&&_c(this.target,this.evTarget,this.domHandler),this.evWin&&_c(qc(this.element),this.evWin,this.domHandler)},A.destroy=function(){this.evEl&&Jc(this.element,this.evEl,this.domHandler),this.evTarget&&Jc(this.target,this.evTarget,this.domHandler),this.evWin&&Jc(qc(this.element),this.evWin,this.domHandler)},g}();function gu(g,A,t){if(g.indexOf&&!t)return g.indexOf(A);for(var C=0;Ct[A]}):C.sort()),C}var nu={touchstart:1,touchmove:2,touchend:4,touchcancel:8},su=function(g){function A(){var t;return A.prototype.evTarget="touchstart touchmove touchend touchcancel",(t=g.apply(this,arguments)||this).targetIds={},t}return nc(A,g),A.prototype.handler=function(g){var A=nu[g.type],t=ru.call(this,g,A);t&&this.callback(this.manager,A,{pointers:t[0],changedPointers:t[1],pointerType:kc,srcEvent:g})},A}($c);function ru(g,A){var t,C,I=iu(g.touches),e=this.targetIds;if(3&A&&1===I.length)return e[I[0].identifier]=!0,[I,I];var i=iu(g.changedTouches),o=[],n=this.target;if(C=I.filter(function(g){return jc(g.target,n)}),1===A)for(t=0;t-1&&C.splice(g,1)},2500)}}function lu(g,A){1&g?(this.primaryTouch=A.changedPointers[0].identifier,hu.call(this,A)):12&g&&hu.call(this,A)}function cu(g){for(var A=g.srcEvent.clientX,t=g.srcEvent.clientY,C=0;C-1&&this.requireFail.splice(A,1),this},A.hasRequireFailures=function(){return this.requireFail.length>0},A.canRecognizeWith=function(g){return!!this.simultaneous[g.id]},A.emit=function(g){var A=this,t=this.state;function C(t){A.manager.emit(t,g)}t<8&&C(A.options.event+mu(t)),C(A.options.event),g.additionalEvent&&C(g.additionalEvent),t>=8&&C(A.options.event+mu(t))},A.tryEmit=function(g){if(this.canEmit())return this.emit(g);this.state=fu},A.canEmit=function(){for(var g=0;gA.threshold&&I&A.direction},t.attrTest=function(g){return xu.prototype.attrTest.call(this,g)&&(2&this.state||!(2&this.state)&&this.directionTest(g))},t.emit=function(A){this.pX=A.deltaX,this.pY=A.deltaY;var t=Eu(A.direction);t&&(A.additionalEvent=this.options.event+t),g.prototype.emit.call(this,A)},A}(xu),Tu=function(g){function A(A){return void 0===A&&(A={}),g.call(this,oc({event:"swipe",threshold:10,velocity:.3,direction:30,pointers:1},A))||this}nc(A,g);var t=A.prototype;return t.getTouchAction=function(){return Ou.prototype.getTouchAction.call(this)},t.attrTest=function(A){var t,C=this.options.direction;return 30&C?t=A.overallVelocity:6&C?t=A.overallVelocityX:C&Mc&&(t=A.overallVelocityY),g.prototype.attrTest.call(this,A)&&C&A.offsetDirection&&A.distance>this.options.threshold&&A.maxPointers===this.options.pointers&&cc(t)>this.options.velocity&&4&A.eventType},t.emit=function(g){var A=Eu(g.offsetDirection);A&&this.manager.emit(this.options.event+A,g),this.manager.emit(this.options.event,g)},A}(xu),Du=function(g){function A(A){return void 0===A&&(A={}),g.call(this,oc({event:"pinch",threshold:0,pointers:2},A))||this}nc(A,g);var t=A.prototype;return t.getTouchAction=function(){return[wc]},t.attrTest=function(A){return g.prototype.attrTest.call(this,A)&&(Math.abs(A.scale-1)>this.options.threshold||2&this.state)},t.emit=function(A){if(1!==A.scale){var t=A.scale<1?"in":"out";A.additionalEvent=this.options.event+t}g.prototype.emit.call(this,A)},A}(xu),Nu=function(g){function A(A){return void 0===A&&(A={}),g.call(this,oc({event:"rotate",threshold:0,pointers:2},A))||this}nc(A,g);var t=A.prototype;return t.getTouchAction=function(){return[wc]},t.attrTest=function(A){return g.prototype.attrTest.call(this,A)&&(Math.abs(A.rotation)>this.options.threshold||2&this.state)},A}(xu),ku=function(g){function A(A){var t;return void 0===A&&(A={}),(t=g.call(this,oc({event:"press",pointers:1,time:251,threshold:9},A))||this)._timer=null,t._input=null,t}nc(A,g);var t=A.prototype;return t.getTouchAction=function(){return[mc]},t.process=function(g){var A=this,t=this.options,C=g.pointers.length===t.pointers,I=g.distancet.time;if(this._input=g,!I||!C||12&g.eventType&&!e)this.reset();else if(1&g.eventType)this.reset(),this._timer=setTimeout(function(){A.state=8,A.tryEmit()},t.time);else if(4&g.eventType)return 8;return fu},t.reset=function(){clearTimeout(this._timer)},t.emit=function(g){8===this.state&&(g&&4&g.eventType?this.manager.emit(this.options.event+"up",g):(this._input.timeStamp=uc(),this.manager.emit(this.options.event,this._input)))},A}(yu),Ru={domEvents:!1,touchAction:bc,enable:!0,inputTarget:null,inputClass:null,cssProps:{userSelect:"none",touchSelect:"none",touchCallout:"none",contentZooming:"none",userDrag:"none",tapHighlightColor:"rgba(0,0,0,0)"}},Pu=[[Nu,{enable:!1}],[Du,{enable:!1},["rotate"]],[Tu,{direction:6}],[Ou,{direction:6},["swipe"]],[wu],[wu,{event:"doubletap",taps:2},["tap"]],[ku]];function Mu(g,A){var t,C=g.element;C.style&&(Zc(g.options.cssProps,function(I,e){t=pc(C.style,e),A?(g.oldCssProps[t]=C.style[t],C.style[t]=I):C.style[t]=g.oldCssProps[t]||""}),A||(g.oldCssProps={}))}var zu=function(){function g(g,A){var t,C=this;this.options=ac({},Ru,A||{}),this.options.inputTarget=this.options.inputTarget||g,this.handlers={},this.session={},this.recognizers=[],this.oldCssProps={},this.element=g,this.input=new((t=this).options.inputClass||(Dc?eu:Nc?su:Tc?uu:du))(t,Hc),this.touchAction=new Gc(this,this.options.touchAction),Mu(this,!0),Zc(this.options.recognizers,function(g){var A=C.add(new g[0](g[1]));g[2]&&A.recognizeWith(g[2]),g[3]&&A.requireFailure(g[3])},this)}var A=g.prototype;return A.set=function(g){return ac(this.options,g),g.touchAction&&this.touchAction.update(),g.inputTarget&&(this.input.destroy(),this.input.target=g.inputTarget,this.input.init()),this},A.stop=function(g){this.session.stopped=g?2:1},A.recognize=function(g){var A=this.session;if(!A.stopped){var t;this.touchAction.preventDefaults(g);var C=this.recognizers,I=A.curRecognizer;(!I||I&&8&I.state)&&(A.curRecognizer=null,I=null);for(var e=0;e\s*\(/gm,"{anonymous}()@"):"Unknown Stack Trace",I=window.console&&(window.console.warn||window.console.log);return I&&I.call(window.console,C,t),g.apply(this,arguments)}}var Gu=Fu(function(g,A,t){for(var C=Object.keys(A),I=0;I2)return Uu(Qu(A[0],A[1]),...$s(A).call(A,2));const C=A[0],I=A[1];if(C instanceof Date&&I instanceof Date)return C.setTime(I.getTime()),C;for(const g of er(I))Object.prototype.propertyIsEnumerable.call(I,g)&&(I[g]===Wu?delete C[g]:null===C[g]||null===I[g]||"object"!=typeof C[g]||"object"!=typeof I[g]||cr(C[g])||cr(I[g])?C[g]=Ku(I[g]):C[g]=Uu(C[g],I[g]));return C}function Ku(g){return cr(g)?Tr(g).call(g,g=>Ku(g)):"object"==typeof g&&null!==g?g instanceof Date?new Date(g.getTime()):Uu({},g):g}function Hu(g){for(const A of Fr(g))g[A]===Wu?delete g[A]:"object"==typeof g[A]&&null!==g[A]&&Hu(g[A])}function Xu(){for(var g=arguments.length,A=new Array(g),t=0;t>>0,C-=g,C*=g,g=C>>>0,C-=g,g+=4294967296*C}return 2.3283064365386963e-10*(g>>>0)}}();let A=g(" "),t=g(" "),C=g(" ");for(let I=0;I{const g=2091639*C+2.3283064365386963e-10*i;return C=I,I=e,e=g-(i=0|g)};return o.uint32=()=>4294967296*o(),o.fract53=()=>o()+11102230246251565e-32*(2097152*o()|0),o.algorithm="Alea",o.seed=g,o.version="0.9",o}(A.length?A:[Aa()])}const _u="undefined"!=typeof window?window.Hammer||Yu:function(){return function(){const g=()=>{};return{on:g,off:g,destroy:g,emit:g,get:()=>({set:g})}}()};function Ju(g){var A;this._cleanupQueue=[],this.active=!1,this._dom={container:g,overlay:document.createElement("div")},this._dom.overlay.classList.add("vis-overlay"),this._dom.container.appendChild(this._dom.overlay),this._cleanupQueue.push(()=>{this._dom.overlay.parentNode.removeChild(this._dom.overlay)});const t=_u(this._dom.overlay);t.on("tap",IC(A=this._onTapOverlay).call(A,this)),this._cleanupQueue.push(()=>{t.destroy()});const C=["tap","doubletap","press","pinch","pan","panstart","panmove","panend"];pa(C).call(C,g=>{t.on(g,g=>{g.srcEvent.stopPropagation()})}),document&&document.body&&(this._onClick=A=>{(function(g,A){for(;g;){if(g===A)return!0;g=g.parentNode}return!1})(A.target,g)||this.deactivate()},document.body.addEventListener("click",this._onClick),this._cleanupQueue.push(()=>{document.body.removeEventListener("click",this._onClick)})),this._escListener=g=>{("key"in g?"Escape"===g.key:27===g.keyCode)&&this.deactivate()}}TC(Ju.prototype),Ju.current=null,Ju.prototype.destroy=function(){this.deactivate();for(const t of Ma(g=Va(A=this._cleanupQueue).call(A,0)).call(g)){var g,A;t()}},Ju.prototype.activate=function(){Ju.current&&Ju.current.deactivate(),Ju.current=this,this.active=!0,this._dom.overlay.style.display="none",this._dom.container.classList.add("vis-active"),this.emit("change"),this.emit("activate"),document.body.addEventListener("keydown",this._escListener)},Ju.prototype.deactivate=function(){this.active=!1,this._dom.overlay.style.display="block",this._dom.container.classList.remove("vis-active"),document.body.removeEventListener("keydown",this._escListener),this.emit("change"),this.emit("deactivate")},Ju.prototype._onTapOverlay=function(g){this.activate(),g.srcEvent.stopPropagation()};const qu=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i,$u=/^#?([a-f\d])([a-f\d])([a-f\d])$/i,gp=/^rgb\( *(1?\d{1,2}|2[0-4]\d|25[0-5]) *, *(1?\d{1,2}|2[0-4]\d|25[0-5]) *, *(1?\d{1,2}|2[0-4]\d|25[0-5]) *\)$/i,Ap=/^rgba\( *(1?\d{1,2}|2[0-4]\d|25[0-5]) *, *(1?\d{1,2}|2[0-4]\d|25[0-5]) *, *(1?\d{1,2}|2[0-4]\d|25[0-5]) *, *([01]|0?\.\d+) *\)$/i;function tp(g){if(g)for(;!0===g.hasChildNodes();){const A=g.firstChild;A&&(tp(A),g.removeChild(A))}}function Cp(g){return g instanceof String||"string"==typeof g}function Ip(g){return"object"==typeof g&&null!==g}function ep(g,A,t,C){let I=!1;!0===C&&(I=null===A[t]&&void 0!==g[t]),I?delete g[t]:g[t]=A[t]}function ip(g,A){let t=arguments.length>2&&void 0!==arguments[2]&&arguments[2];for(const C in g)if(void 0!==A[C])if(null===A[C]||"object"!=typeof A[C])ep(g,A,C,t);else{const I=g[C],e=A[C];Ip(I)&&Ip(e)&&ip(I,e,t)}}function op(g,A,t){let C=arguments.length>3&&void 0!==arguments[3]&&arguments[3];if(cr(t))throw new TypeError("Arrays are not supported by deepExtend");for(let I=0;I3&&void 0!==arguments[3]&&arguments[3];if(cr(t))throw new TypeError("Arrays are not supported by deepExtend");for(const I in t)if(Object.prototype.hasOwnProperty.call(t,I)&&!bd(g).call(g,I))if(t[I]&&t[I].constructor===Object)void 0===A[I]&&(A[I]={}),A[I].constructor===Object?sp(A[I],t[I]):ep(A,t,I,C);else if(cr(t[I])){A[I]=[];for(let g=0;g2&&void 0!==arguments[2]&&arguments[2],C=arguments.length>3&&void 0!==arguments[3]&&arguments[3];for(const e in A)if(Object.prototype.hasOwnProperty.call(A,e)||!0===t)if("object"==typeof A[e]&&null!==A[e]&&Pd(A[e])===Object.prototype)void 0===g[e]?g[e]=sp({},A[e],t):"object"==typeof g[e]&&null!==g[e]&&Pd(g[e])===Object.prototype?sp(g[e],A[e],t):ep(g,A,e,C);else if(cr(A[e])){var I;g[e]=$s(I=A[e]).call(I)}else ep(g,A,e,C);return g}function rp(g,A){return[...g,A]}function ap(g){return $s(g).call(g)}function dp(g){return g.getBoundingClientRect().top}function hp(g,A){if(cr(g)){const t=g.length;for(let C=0;C3&&void 0!==arguments[3]?arguments[3]:{};const I=function(g){return null!=g},e=function(g){return null!==g&&"object"==typeof g};if(!e(g))throw new Error("Parameter mergeTarget must be an object");if(!e(A))throw new Error("Parameter options must be an object");if(!I(t))throw new Error("Parameter option must have a value");if(!e(C))throw new Error("Parameter globalOptions must be an object");const i=A[t],o=e(C)&&!function(g){for(const A in g)if(Object.prototype.hasOwnProperty.call(g,A))return!1;return!0}(C)?C[t]:void 0,n=o?o.enabled:void 0;if(void 0===i)return;if("boolean"==typeof i)return e(g[t])||(g[t]={}),void(g[t].enabled=i);if(null===i&&!e(g[t])){if(!I(o))return;g[t]=hl(o)}if(!e(i))return;let s=!0;void 0!==i.enabled?s=i.enabled:void 0!==n&&(s=o.enabled),function(g,A,t){e(g[t])||(g[t]={});const C=A[t],I=g[t];for(const g in C)Object.prototype.hasOwnProperty.call(C,g)&&(I[g]=C[g])}(g,A,t),g[t].enabled=s}const Ep={linear:g=>g,easeInQuad:g=>g*g,easeOutQuad:g=>g*(2-g),easeInOutQuad:g=>g<.5?2*g*g:(4-2*g)*g-1,easeInCubic:g=>g*g*g,easeOutCubic:g=>--g*g*g+1,easeInOutCubic:g=>g<.5?4*g*g*g:(g-1)*(2*g-2)*(2*g-2)+1,easeInQuart:g=>g*g*g*g,easeOutQuart:g=>1- --g*g*g*g,easeInOutQuart:g=>g<.5?8*g*g*g*g:1-8*--g*g*g*g,easeInQuint:g=>g*g*g*g*g,easeOutQuint:g=>1+--g*g*g*g*g,easeInOutQuint:g=>g<.5?16*g*g*g*g*g:1+16*--g*g*g*g*g};function Op(g,A){let t;cr(A)||(A=[A]);for(const C of g)if(C){t=C[A[0]];for(let g=1;g0&&void 0!==arguments[0]?arguments[0]:1;this.pixelRatio=g,this.generated=!1,this.centerCoordinates={x:144.5,y:144.5},this.r=289*.49,this.color={r:255,g:255,b:255,a:1},this.hueCircle=void 0,this.initialColor={r:255,g:255,b:255,a:1},this.previousColor=void 0,this.applied=!1,this.updateCallback=()=>{},this.closeCallback=()=>{},this._create()}insertTo(g){void 0!==this.hammer&&(this.hammer.destroy(),this.hammer=void 0),this.container=g,this.container.appendChild(this.frame),this._bindHammer(),this._setSize()}setUpdateCallback(g){if("function"!=typeof g)throw new Error("Function attempted to set as colorPicker update callback is not a function.");this.updateCallback=g}setCloseCallback(g){if("function"!=typeof g)throw new Error("Function attempted to set as colorPicker closing callback is not a function.");this.closeCallback=g}_isColorString(g){if("string"==typeof g)return Tp[g]}setColor(g){let A,t=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];if("none"===g)return;const C=this._isColorString(g);if(void 0!==C&&(g=C),!0===Cp(g)){if(!0===yp(g)){const t=g.substr(4).substr(0,g.length-5).split(",");A={r:t[0],g:t[1],b:t[2],a:1}}else if(!0===function(g){return Ap.test(g)}(g)){const t=g.substr(5).substr(0,g.length-6).split(",");A={r:t[0],g:t[1],b:t[2],a:t[3]}}else if(!0===mp(g)){const t=lp(g);A={r:t.r,g:t.g,b:t.b,a:1}}}else if(g instanceof Object&&void 0!==g.r&&void 0!==g.g&&void 0!==g.b){const t=void 0!==g.a?g.a:"1.0";A={r:g.r,g:g.g,b:g.b,a:t}}if(void 0===A)throw new Error("Unknown color passed to the colorPicker. Supported are strings: rgb, hex, rgba. Object: rgb ({r:r,g:g,b:b,[a:a]}). Supplied: "+Tl(g));this._setColor(A,t)}show(){void 0!==this.closeCallback&&(this.closeCallback(),this.closeCallback=void 0),this.applied=!1,this.frame.style.display="block",this._generateHueCircle()}_hide(){!0===(!(arguments.length>0&&void 0!==arguments[0])||arguments[0])&&(this.previousColor=_t({},this.color)),!0===this.applied&&this.updateCallback(this.initialColor),this.frame.style.display="none",$l(()=>{void 0!==this.closeCallback&&(this.closeCallback(),this.closeCallback=void 0)},0)}_save(){this.updateCallback(this.color),this.applied=!1,this._hide()}_apply(){this.applied=!0,this.updateCallback(this.color),this._updatePicker(this.color)}_loadLast(){void 0!==this.previousColor?this.setColor(this.previousColor,!1):alert("There is no last color to load...")}_setColor(g){!0===(!(arguments.length>1&&void 0!==arguments[1])||arguments[1])&&(this.initialColor=_t({},g)),this.color=g;const A=fp(g.r,g.g,g.b),t=2*Math.PI,C=this.r*A.s,I=this.centerCoordinates.x+C*Math.sin(t*A.h),e=this.centerCoordinates.y+C*Math.cos(t*A.h);this.colorPickerSelector.style.left=I-.5*this.colorPickerSelector.clientWidth+"px",this.colorPickerSelector.style.top=e-.5*this.colorPickerSelector.clientHeight+"px",this._updatePicker(g)}_setOpacity(g){this.color.a=g/100,this._updatePicker(this.color)}_setBrightness(g){const A=fp(this.color.r,this.color.g,this.color.b);A.v=g/100;const t=vp(A.h,A.s,A.v);t.a=this.color.a,this.color=t,this._updatePicker()}_updatePicker(){let g=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.color;const A=fp(g.r,g.g,g.b),t=this.colorPickerCanvas.getContext("2d");void 0===this.pixelRation&&(this.pixelRatio=(window.devicePixelRatio||1)/(t.webkitBackingStorePixelRatio||t.mozBackingStorePixelRatio||t.msBackingStorePixelRatio||t.oBackingStorePixelRatio||t.backingStorePixelRatio||1)),t.setTransform(this.pixelRatio,0,0,this.pixelRatio,0,0);const C=this.colorPickerCanvas.clientWidth,I=this.colorPickerCanvas.clientHeight;t.clearRect(0,0,C,I),t.putImageData(this.hueCircle,0,0),t.fillStyle="rgba(0,0,0,"+(1-A.v)+")",t.circle(this.centerCoordinates.x,this.centerCoordinates.y,this.r),ic(t).call(t),this.brightnessRange.value=100*A.v,this.opacityRange.value=100*g.a,this.initialColorDiv.style.backgroundColor="rgba("+this.initialColor.r+","+this.initialColor.g+","+this.initialColor.b+","+this.initialColor.a+")",this.newColorDiv.style.backgroundColor="rgba("+this.color.r+","+this.color.g+","+this.color.b+","+this.color.a+")"}_setSize(){this.colorPickerCanvas.style.width="100%",this.colorPickerCanvas.style.height="100%",this.colorPickerCanvas.width=289*this.pixelRatio,this.colorPickerCanvas.height=289*this.pixelRatio}_create(){var g,A,t,C;if(this.frame=document.createElement("div"),this.frame.className="vis-color-picker",this.colorPickerDiv=document.createElement("div"),this.colorPickerSelector=document.createElement("div"),this.colorPickerSelector.className="vis-selector",this.colorPickerDiv.appendChild(this.colorPickerSelector),this.colorPickerCanvas=document.createElement("canvas"),this.colorPickerDiv.appendChild(this.colorPickerCanvas),this.colorPickerCanvas.getContext){const g=this.colorPickerCanvas.getContext("2d");this.pixelRatio=(window.devicePixelRatio||1)/(g.webkitBackingStorePixelRatio||g.mozBackingStorePixelRatio||g.msBackingStorePixelRatio||g.oBackingStorePixelRatio||g.backingStorePixelRatio||1),this.colorPickerCanvas.getContext("2d").setTransform(this.pixelRatio,0,0,this.pixelRatio,0,0)}else{const g=document.createElement("DIV");g.style.color="red",g.style.fontWeight="bold",g.style.padding="10px",g.innerText="Error: your browser does not support HTML canvas",this.colorPickerCanvas.appendChild(g)}this.colorPickerDiv.className="vis-color",this.opacityDiv=document.createElement("div"),this.opacityDiv.className="vis-opacity",this.brightnessDiv=document.createElement("div"),this.brightnessDiv.className="vis-brightness",this.arrowDiv=document.createElement("div"),this.arrowDiv.className="vis-arrow",this.opacityRange=document.createElement("input");try{this.opacityRange.type="range",this.opacityRange.min="0",this.opacityRange.max="100"}catch(g){}this.opacityRange.value="100",this.opacityRange.className="vis-range",this.brightnessRange=document.createElement("input");try{this.brightnessRange.type="range",this.brightnessRange.min="0",this.brightnessRange.max="100"}catch(g){}this.brightnessRange.value="100",this.brightnessRange.className="vis-range",this.opacityDiv.appendChild(this.opacityRange),this.brightnessDiv.appendChild(this.brightnessRange);const I=this;this.opacityRange.onchange=function(){I._setOpacity(this.value)},this.opacityRange.oninput=function(){I._setOpacity(this.value)},this.brightnessRange.onchange=function(){I._setBrightness(this.value)},this.brightnessRange.oninput=function(){I._setBrightness(this.value)},this.brightnessLabel=document.createElement("div"),this.brightnessLabel.className="vis-label vis-brightness",this.brightnessLabel.innerText="brightness:",this.opacityLabel=document.createElement("div"),this.opacityLabel.className="vis-label vis-opacity",this.opacityLabel.innerText="opacity:",this.newColorDiv=document.createElement("div"),this.newColorDiv.className="vis-new-color",this.newColorDiv.innerText="new",this.initialColorDiv=document.createElement("div"),this.initialColorDiv.className="vis-initial-color",this.initialColorDiv.innerText="initial",this.cancelButton=document.createElement("div"),this.cancelButton.className="vis-button vis-cancel",this.cancelButton.innerText="cancel",this.cancelButton.onclick=IC(g=this._hide).call(g,this,!1),this.applyButton=document.createElement("div"),this.applyButton.className="vis-button vis-apply",this.applyButton.innerText="apply",this.applyButton.onclick=IC(A=this._apply).call(A,this),this.saveButton=document.createElement("div"),this.saveButton.className="vis-button vis-save",this.saveButton.innerText="save",this.saveButton.onclick=IC(t=this._save).call(t,this),this.loadButton=document.createElement("div"),this.loadButton.className="vis-button vis-load",this.loadButton.innerText="load last",this.loadButton.onclick=IC(C=this._loadLast).call(C,this),this.frame.appendChild(this.colorPickerDiv),this.frame.appendChild(this.arrowDiv),this.frame.appendChild(this.brightnessLabel),this.frame.appendChild(this.brightnessDiv),this.frame.appendChild(this.opacityLabel),this.frame.appendChild(this.opacityDiv),this.frame.appendChild(this.newColorDiv),this.frame.appendChild(this.initialColorDiv),this.frame.appendChild(this.cancelButton),this.frame.appendChild(this.applyButton),this.frame.appendChild(this.saveButton),this.frame.appendChild(this.loadButton)}_bindHammer(){this.drag={},this.pinch={},this.hammer=new _u(this.colorPickerCanvas),this.hammer.get("pinch").set({enable:!0}),this.hammer.on("hammer.input",g=>{g.isFirst&&this._moveSelector(g)}),this.hammer.on("tap",g=>{this._moveSelector(g)}),this.hammer.on("panstart",g=>{this._moveSelector(g)}),this.hammer.on("panmove",g=>{this._moveSelector(g)}),this.hammer.on("panend",g=>{this._moveSelector(g)})}_generateHueCircle(){if(!1===this.generated){const g=this.colorPickerCanvas.getContext("2d");void 0===this.pixelRation&&(this.pixelRatio=(window.devicePixelRatio||1)/(g.webkitBackingStorePixelRatio||g.mozBackingStorePixelRatio||g.msBackingStorePixelRatio||g.oBackingStorePixelRatio||g.backingStorePixelRatio||1)),g.setTransform(this.pixelRatio,0,0,this.pixelRatio,0,0);const A=this.colorPickerCanvas.clientWidth,t=this.colorPickerCanvas.clientHeight;let C,I,e,i;g.clearRect(0,0,A,t),this.centerCoordinates={x:.5*A,y:.5*t},this.r=.49*A;const o=2*Math.PI/360,n=1/360,s=1/this.r;let r;for(e=0;e<360;e++)for(i=0;i3&&void 0!==arguments[3]?arguments[3]:1,I=arguments.length>4&&void 0!==arguments[4]?arguments[4]:()=>!1;this.parent=g,this.changedOptions=[],this.container=A,this.allowCreation=!1,this.hideOption=I,this.options={},this.initialized=!1,this.popupCounter=0,this.defaultOptions={enabled:!1,filter:!0,container:void 0,showButton:!0},_t(this.options,this.defaultOptions),this.configureOptions=t,this.moduleOptions={},this.domElements=[],this.popupDiv={},this.popupLimit=5,this.popupHistory={},this.colorPicker=new Dp(C),this.wrapper=void 0}setOptions(g){if(void 0!==g){this.popupHistory={},this._removePopup();let A=!0;if("string"==typeof g)this.options.filter=g;else if(cr(g))this.options.filter=g.join();else if("object"==typeof g){if(null==g)throw new TypeError("options cannot be null");void 0!==g.container&&(this.options.container=g.container),void 0!==eh(g)&&(this.options.filter=eh(g)),void 0!==g.showButton&&(this.options.showButton=g.showButton),void 0!==g.enabled&&(A=g.enabled)}else"boolean"==typeof g?(this.options.filter=!0,A=g):"function"==typeof g&&(this.options.filter=g,A=!0);!1===eh(this.options)&&(A=!1),this.options.enabled=A}this._clean()}setModuleOptions(g){this.moduleOptions=g,!0===this.options.enabled&&(this._clean(),void 0!==this.options.container&&(this.container=this.options.container),this._create())}_create(){this._clean(),this.changedOptions=[];const g=eh(this.options);let A=0,t=!1;for(const C in this.configureOptions)Object.prototype.hasOwnProperty.call(this.configureOptions,C)&&(this.allowCreation=!1,t=!1,"function"==typeof g?(t=g(C,[]),t=t||this._handleObject(this.configureOptions[C],[C],!0)):!0!==g&&-1===Jh(g).call(g,C)||(t=!0),!1!==t&&(this.allowCreation=!0,A>0&&this._makeItem([]),this._makeHeader(C),this._handleObject(this.configureOptions[C],[C])),A++);this._makeButton(),this._push()}_push(){this.wrapper=document.createElement("div"),this.wrapper.className="vis-configuration-wrapper",this.container.appendChild(this.wrapper);for(let g=0;g1?A-1:0),C=1;C{I.appendChild(g)}),this.domElements.push(I),this.domElements.length}return 0}_makeHeader(g){const A=document.createElement("div");A.className="vis-configuration vis-config-header",A.innerText=g,this._makeItem([],A)}_makeLabel(g,A){let t=arguments.length>2&&void 0!==arguments[2]&&arguments[2];const C=document.createElement("div");if(C.className="vis-configuration vis-config-label vis-config-s"+A.length,!0===t){for(;C.firstChild;)C.removeChild(C.firstChild);C.appendChild(Np("i","b",g))}else C.innerText=g+":";return C}_makeDropdown(g,A,t){const C=document.createElement("select");C.className="vis-configuration vis-config-select";let I=0;void 0!==A&&-1!==Jh(g).call(g,A)&&(I=Jh(g).call(g,A));for(let A=0;Ae&&1!==e&&(o.max=Math.ceil(A*g),s=o.max,n="range increased"),o.value=A}else o.value=C;const r=document.createElement("input");r.className="vis-configuration vis-config-rangeinput",r.value=o.value;const a=this;o.onchange=function(){r.value=this.value,a._update(Number(this.value),t)},o.oninput=function(){r.value=this.value};const d=this._makeLabel(t[t.length-1],t),h=this._makeItem(t,d,o,r);""!==n&&this.popupHistory[h]!==s&&(this.popupHistory[h]=s,this._setupPopup(n,h))}_makeButton(){if(!0===this.options.showButton){const g=document.createElement("div");g.className="vis-configuration vis-config-button",g.innerText="generate options",g.onclick=()=>{this._printOptions()},g.onmouseover=()=>{g.className="vis-configuration vis-config-button hover"},g.onmouseout=()=>{g.className="vis-configuration vis-config-button"},this.optionsContainer=document.createElement("div"),this.optionsContainer.className="vis-configuration vis-config-option-container",this.domElements.push(this.optionsContainer),this.domElements.push(g)}}_setupPopup(g,A){if(!0===this.initialized&&!0===this.allowCreation&&this.popupCounter{this._removePopup()},this.popupCounter+=1,this.popupDiv={html:t,index:A}}}_removePopup(){void 0!==this.popupDiv.html&&(this.popupDiv.html.parentNode.removeChild(this.popupDiv.html),clearTimeout(this.popupDiv.hideTimeout),clearTimeout(this.popupDiv.deleteTimeout),this.popupDiv={})}_showPopupIfNeeded(){if(void 0!==this.popupDiv.html){const g=this.domElements[this.popupDiv.index].getBoundingClientRect();this.popupDiv.html.style.left=g.left+"px",this.popupDiv.html.style.top=g.top-30+"px",document.body.appendChild(this.popupDiv.html),this.popupDiv.hideTimeout=$l(()=>{this.popupDiv.html.style.opacity=0},1500),this.popupDiv.deleteTimeout=$l(()=>{this._removePopup()},1800)}}_makeCheckbox(g,A,t){const C=document.createElement("input");C.type="checkbox",C.className="vis-configuration vis-config-checkbox",C.checked=g,void 0!==A&&(C.checked=A,A!==g&&("object"==typeof g?A!==g.enabled&&this.changedOptions.push({path:t,value:A}):this.changedOptions.push({path:t,value:A})));const I=this;C.onchange=function(){I._update(this.checked,t)};const e=this._makeLabel(t[t.length-1],t);this._makeItem(t,e,C)}_makeTextInput(g,A,t){const C=document.createElement("input");C.type="text",C.className="vis-configuration vis-config-text",C.value=A,A!==g&&this.changedOptions.push({path:t,value:A});const I=this;C.onchange=function(){I._update(this.value,t)};const e=this._makeLabel(t[t.length-1],t);this._makeItem(t,e,C)}_makeColorField(g,A,t){const C=g[1],I=document.createElement("div");"none"!==(A=void 0===A?C:A)?(I.className="vis-configuration vis-config-colorBlock",I.style.backgroundColor=A):I.className="vis-configuration vis-config-colorBlock none",A=void 0===A?C:A,I.onclick=()=>{this._showColorPicker(A,I,t)};const e=this._makeLabel(t[t.length-1],t);this._makeItem(t,e,I)}_showColorPicker(g,A,t){A.onclick=function(){},this.colorPicker.insertTo(A),this.colorPicker.show(),this.colorPicker.setColor(g),this.colorPicker.setUpdateCallback(g=>{const C="rgba("+g.r+","+g.g+","+g.b+","+g.a+")";A.style.backgroundColor=C,this._update(C,t)}),this.colorPicker.setCloseCallback(()=>{A.onclick=()=>{this._showColorPicker(g,A,t)}})}_handleObject(g){let A=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[],t=arguments.length>2&&void 0!==arguments[2]&&arguments[2],C=!1;const I=eh(this.options);let e=!1;for(const i in g)if(Object.prototype.hasOwnProperty.call(g,i)){C=!0;const o=g[i],n=rp(A,i);if("function"==typeof I&&(C=I(i,A),!1===C&&!cr(o)&&"string"!=typeof o&&"boolean"!=typeof o&&o instanceof Object&&(this.allowCreation=!1,C=this._handleObject(o,n,!0),this.allowCreation=!1===t)),!1!==C){e=!0;const g=this._getValue(n);if(cr(o))this._handleArray(o,g,n);else if("string"==typeof o)this._makeTextInput(o,g,n);else if("boolean"==typeof o)this._makeCheckbox(o,g,n);else if(o instanceof Object){if(!this.hideOption(A,i,this.moduleOptions))if(void 0!==o.enabled){const g=rp(n,"enabled"),A=this._getValue(g);if(!0===A){const g=this._makeLabel(i,n,!0);this._makeItem(n,g),e=this._handleObject(o,n)||e}else this._makeCheckbox(o,A,n)}else{const g=this._makeLabel(i,n,!0);this._makeItem(n,g),e=this._handleObject(o,n)||e}}else console.error("dont know how to handle",o,i,n)}}return e}_handleArray(g,A,t){"string"==typeof g[0]&&"color"===g[0]?(this._makeColorField(g,A,t),g[1]!==A&&this.changedOptions.push({path:t,value:A})):"string"==typeof g[0]?(this._makeDropdown(g,A,t),g[0]!==A&&this.changedOptions.push({path:t,value:A})):"number"==typeof g[0]&&(this._makeRange(g,A,t),g[0]!==A&&this.changedOptions.push({path:t,value:Number(A)}))}_update(g,A){const t=this._constructOptions(g,A);this.parent.body&&this.parent.body.emitter&&this.parent.body.emitter.emit&&this.parent.body.emitter.emit("configChange",t),this.initialized=!0,this.parent.setOptions(t)}_constructOptions(g,A){let t=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},C=t;g="false"!==(g="true"===g||g)&&g;for(let t=0;tC-this.padding&&(t=!0),I=t?this.x-A:this.x,e=i?this.y-g:this.y}else e=this.y-g,e+g+this.padding>t&&(e=t-g-this.padding),eC&&(I=C-A-this.padding),Ie.distance?" in "+g.printLocation(I.path,A,"")+"Perhaps it was misplaced? Matching option found at: "+g.printLocation(e.path,e.closestMatch,""):I.distance<=8?'. Did you mean "'+I.closestMatch+'"?'+g.printLocation(I.path,A):". Did you mean one of these: "+g.print(Fr(t))+g.printLocation(C,A),console.error('%cUnknown option detected: "'+A+'"'+i,Pp),Rp=!0}static findInOptions(A,t,C){let I=arguments.length>3&&void 0!==arguments[3]&&arguments[3],e=1e9,i="",o=[];const n=A.toLowerCase();let s;for(const a in t){let d;if(void 0!==t[a].__type__&&!0===I){const I=g.findInOptions(A,t[a],rp(C,a));e>I.distance&&(i=I.closestMatch,o=I.path,e=I.distance,s=I.indexMatch)}else{var r;-1!==Jh(r=a.toLowerCase()).call(r,n)&&(s=a),d=g.levenshteinDistance(A,a),e>d&&(i=a,o=ap(C),e=d)}}return{closestMatch:i,path:o,distance:e,indexMatch:s}}static printLocation(g,A){let t="\n\n"+(arguments.length>2&&void 0!==arguments[2]?arguments[2]:"Problem value found at: \n")+"options = {\n";for(let A=0;A":!0,"--":!0};let Up="",Kp=0,Hp="",Xp="",_p=Lp;function Jp(){Kp++,Hp=Up.charAt(Kp)}function qp(){return Up.charAt(Kp+1)}function $p(g){const A=g.charCodeAt(0);return A<47?35===A||46===A:A<59?A>47:A<91?A>64:A<96?95===A:A<123&&A>96}function gf(g,A){if(g||(g={}),A)for(const t in A)A.hasOwnProperty(t)&&(g[t]=A[t]);return g}function Af(g,A,t){const C=A.split(".");let I=g;for(;C.length;){const g=C.shift();C.length?(I[g]||(I[g]={}),I=I[g]):I[g]=t}}function tf(g,A){let t,C,I=null;const e=[g];let i=g;for(;i.parent;)e.push(i.parent),i=i.parent;if(i.nodes)for(t=0,C=i.nodes.length;t=0;t--){var o;const g=e[t];g.nodes||(g.nodes=[]),-1===Jh(o=g.nodes).call(o,I)&&g.nodes.push(I)}A.attr&&(I.attr=gf(I.attr,A.attr))}function Cf(g,A){if(g.edges||(g.edges=[]),g.edges.push(A),g.edge){const t=gf({},g.edge);A.attr=gf(t,A.attr)}}function If(g,A,t,C,I){const e={from:A,to:t,type:C};return g.edge&&(e.attr=gf({},g.edge)),e.attr=gf(e.attr||{},I),null!=I&&I.hasOwnProperty("arrows")&&null!=I.arrows&&(e.arrows={to:{enabled:!0,type:I.arrows.type}},I.arrows=null),e}function ef(){for(_p=Lp,Xp="";" "===Hp||"\t"===Hp||"\n"===Hp||"\r"===Hp;)Jp();let g;do{if(g=!1,"#"===Hp){let A=Kp-1;for(;" "===Up.charAt(A)||"\t"===Up.charAt(A);)A--;if("\n"===Up.charAt(A)||""===Up.charAt(A)){for(;""!=Hp&&"\n"!=Hp;)Jp();g=!0}}if("/"===Hp&&"/"===qp()){for(;""!=Hp&&"\n"!=Hp;)Jp();g=!0}if("/"===Hp&&"*"===qp()){for(;""!=Hp;){if("*"===Hp&&"/"===qp()){Jp(),Jp();break}Jp()}g=!0}for(;" "===Hp||"\t"===Hp||"\n"===Hp||"\r"===Hp;)Jp()}while(g);if(""===Hp)return void(_p=Vp);const A=Hp+qp();if(Qp[A])return _p=Vp,Xp=A,Jp(),void Jp();if(Qp[Hp])return _p=Vp,Xp=Hp,void Jp();if($p(Hp)||"-"===Hp){for(Xp+=Hp,Jp();$p(Hp);)Xp+=Hp,Jp();return"false"===Xp?Xp=!1:"true"===Xp?Xp=!0:isNaN(Number(Xp))||(Xp=Number(Xp)),void(_p=Yp)}if('"'===Hp){for(Jp();""!=Hp&&('"'!=Hp||'"'===Hp&&'"'===qp());)'"'===Hp?(Xp+=Hp,Jp()):"\\"===Hp&&"n"===qp()?(Xp+="\n",Jp()):Xp+=Hp,Jp();if('"'!=Hp)throw hf('End of string " expected');return Jp(),void(_p=Yp)}for(_p=Wp;""!=Hp;)Xp+=Hp,Jp();throw new SyntaxError('Syntax error in part "'+lf(Xp,30)+'"')}function of(){const g={};if(Kp=0,Hp=Up.charAt(0),ef(),"strict"===Xp&&(g.strict=!0,ef()),"graph"!==Xp&&"digraph"!==Xp||(g.type=Xp,ef()),_p===Yp&&(g.id=Xp,ef()),"{"!=Xp)throw hf("Angle bracket { expected");if(ef(),nf(g),"}"!=Xp)throw hf("Angle bracket } expected");if(ef(),""!==Xp)throw hf("End of file expected");return ef(),delete g.node,delete g.edge,delete g.graph,g}function nf(g){for(;""!==Xp&&"}"!=Xp;)sf(g),";"===Xp&&ef()}function sf(g){const A=rf(g);if(A)return void af(g,A);const t=function(g){if("node"===Xp)return ef(),g.node=df(),"node";if("edge"===Xp)return ef(),g.edge=df(),"edge";if("graph"===Xp)return ef(),g.graph=df(),"graph";return null}(g);if(t)return;if(_p!=Yp)throw hf("Identifier expected");const C=Xp;if(ef(),"="===Xp){if(ef(),_p!=Yp)throw hf("Identifier expected");g[C]=Xp,ef()}else!function(g,A){const t={id:A},C=df();C&&(t.attr=C);tf(g,t),af(g,A)}(g,C)}function rf(g){let A=null;if("subgraph"===Xp&&(A={},A.type="subgraph",ef(),_p===Yp&&(A.id=Xp,ef())),"{"===Xp){if(ef(),A||(A={}),A.parent=g,A.node=g.node,A.edge=g.edge,A.graph=g.graph,nf(A),"}"!=Xp)throw hf("Angle bracket } expected");ef(),delete A.node,delete A.edge,delete A.graph,delete A.parent,g.subgraphs||(g.subgraphs=[]),g.subgraphs.push(A)}return A}function af(g,A){for(;"->"===Xp||"--"===Xp;){let t;const C=Xp;ef();const I=rf(g);if(I)t=I;else{if(_p!=Yp)throw hf("Identifier or subgraph expected");t=Xp,tf(g,{id:t}),ef()}Cf(g,If(g,A,t,C,df())),A=t}}function df(){let g,A=null;const t={dashed:!0,solid:!1,dotted:[1,5]},C={dot:"circle",box:"box",crow:"crow",curve:"curve",icurve:"inv_curve",normal:"triangle",inv:"inv_triangle",diamond:"diamond",tee:"bar",vee:"vee"};let I=[];const e=[];for(;"["===Xp;){for(ef(),A={};""!==Xp&&"]"!=Xp;){if(_p!=Yp)throw hf("Attribute name expected");let g=Xp;if(ef(),"="!=Xp)throw hf("Equal sign = expected");if(ef(),_p!=Yp)throw hf("Attribute value expected");let i,o=Xp;"style"===g&&(o=t[o]),"arrowhead"===g&&(i=C[o],g="arrows",o={to:{enabled:!0,type:i}}),"arrowtail"===g&&(i=C[o],g="arrows",o={from:{enabled:!0,type:i}}),I.push({attr:A,name:g,value:o}),e.push(g),ef(),","==Xp&&ef()}if("]"!=Xp)throw hf("Bracket ] expected");ef()}if(bd(e).call(e,"dir")){const g={arrows:{}};for(let A=0;A"===g.type&&(A.arrows="to"),A};pa(I=A.edges).call(I,function(A){let C,I;var e,i,o,n,s;(C=A.from instanceof Object?A.from.nodes:{id:A.from},I=A.to instanceof Object?A.to.nodes:{id:A.to},A.from instanceof Object&&A.from.edges)&&pa(e=A.from.edges).call(e,function(A){const C=g(A);t.edges.push(C)});(o=I,n=function(C,I){const e=If(t,C.id,I.id,A.type,A.attr),i=g(e);t.edges.push(i)},cr(i=C)?pa(i).call(i,function(g){cr(o)?pa(o).call(o,function(A){n(g,A)}):n(g,o)}):cr(o)?pa(o).call(o,function(g){n(i,g)}):n(i,o),A.to instanceof Object&&A.to.edges)&&pa(s=A.to.edges).call(s,function(A){const C=g(A);t.edges.push(C)})})}return A.attr&&(t.options=A.attr),t}function ff(g){return Up=g,of()}var vf=Object.freeze({__proto__:null,DOTToGraph:pf,parseDOT:ff});function bf(g){var A;let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},C=t.fixed,I=void 0!==C&&C,e=t.inheritColor,i=void 0!==e&&e,o=t.parseColor,n=void 0!==o&&o;const s=g.edges,r=Tr(s).call(s,g=>{const A={from:g.source,id:g.id,to:g.target};return null!=g.attributes&&(A.attributes=g.attributes),null!=g.label&&(A.label=g.label),null!=g.attributes&&null!=g.attributes.title&&(A.title=g.attributes.title),"Directed"===g.type&&(A.arrows="to"),g.color&&!1===i&&(A.color=g.color),A});return{nodes:Tr(A=g.nodes).call(A,g=>{const A={id:g.id,fixed:I&&null!=g.x&&null!=g.y};return null!=g.attributes&&(A.attributes=g.attributes),null!=g.label&&(A.label=g.label),null!=g.size&&(A.size=g.size),null!=g.attributes&&null!=g.attributes.title&&(A.title=g.attributes.title),null!=g.title&&(A.title=g.title),null!=g.x&&(A.x=g.x),null!=g.y&&(A.y=g.y),null!=g.color&&(A.color=!0===n?g.color:{background:g.color,border:g.color,highlight:{background:g.color,border:g.color},hover:{background:g.color,border:g.color}}),A}),edges:r}}var mf=Object.freeze({__proto__:null,parseGephi:bf});class yf{constructor(){this.NUM_ITERATIONS=4,this.image=new Image,this.canvas=document.createElement("canvas")}init(){if(this.initialized())return;this.src=this.image.src;const g=this.image.width,A=this.image.height;this.width=g,this.height=A;const t=Math.floor(A/2),C=Math.floor(A/4),I=Math.floor(A/8),e=Math.floor(A/16),i=Math.floor(g/2),o=Math.floor(g/4),n=Math.floor(g/8),s=Math.floor(g/16);this.canvas.width=3*o,this.canvas.height=t,this.coordinates=[[0,0,i,t],[i,0,o,C],[i,C,n,I],[5*n,C,s,e]],this._fillMipMap()}initialized(){return void 0!==this.coordinates}_fillMipMap(){const g=this.canvas.getContext("2d"),A=this.coordinates[0];g.drawImage(this.image,A[0],A[1],A[2],A[3]);for(let A=1;A2){A*=.5;let i=0;for(;A>2&&i=this.NUM_ITERATIONS&&(i=this.NUM_ITERATIONS-1);const o=this.coordinates[i];g.drawImage(this.canvas,o[0],o[1],o[2],o[3],t,C,I,e)}else g.drawImage(this.image,t,C,I,e)}}class wf{constructor(g){this.images={},this.imageBroken={},this.callback=g}_tryloadBrokenUrl(g,A,t){void 0!==g&&void 0!==t&&(void 0!==A?(t.image.addEventListener("error",()=>{console.error("Could not load brokenImage:",A)}),t.image.src=A):console.warn("No broken url image defined"))}_redrawWithImage(g){this.callback&&this.callback(g)}load(g,A){const t=this.images[g];if(t)return t;const C=new yf;return this.images[g]=C,C.image.addEventListener("load",()=>{this._fixImageCoordinates(C.image),C.init(),this._redrawWithImage(C)}),C.image.addEventListener("error",()=>{console.error("Could not load image:",g),this._tryloadBrokenUrl(g,A,C)}),C.image.src=g,C}_fixImageCoordinates(g){0===g.width&&(document.body.appendChild(g),g.width=g.offsetWidth,g.height=g.offsetHeight,document.body.removeChild(g))}}var xf,Ef,Of,Tf,Df=Object.freeze({__proto__:null,cn:{addDescription:"单击空白处放置新节点。",addEdge:"添加连接线",addNode:"添加节点",back:"返回",close:"關閉",createEdgeError:"无法将连接线连接到群集。",del:"删除选定",deleteClusterError:"无法删除群集。",edgeDescription:"单击某个节点并将该连接线拖动到另一个节点以连接它们。",edit:"编辑",editClusterError:"无法编辑群集。",editEdge:"编辑连接线",editEdgeDescription:"单击控制节点并将它们拖到节点上连接。",editNode:"编辑节点"},cs:{addDescription:"Kluknutím do prázdného prostoru můžete přidat nový vrchol.",addEdge:"Přidat hranu",addNode:"Přidat vrchol",back:"Zpět",close:"Zavřít",createEdgeError:"Nelze připojit hranu ke shluku.",del:"Smazat výběr",deleteClusterError:"Nelze mazat shluky.",edgeDescription:"Přetažením z jednoho vrcholu do druhého můžete spojit tyto vrcholy novou hranou.",edit:"Upravit",editClusterError:"Nelze upravovat shluky.",editEdge:"Upravit hranu",editEdgeDescription:"Přetažením kontrolního vrcholu hrany ji můžete připojit k jinému vrcholu.",editNode:"Upravit vrchol"},de:{addDescription:"Klicke auf eine freie Stelle, um einen neuen Knoten zu plazieren.",addEdge:"Kante hinzufügen",addNode:"Knoten hinzufügen",back:"Zurück",close:"Schließen",createEdgeError:"Es ist nicht möglich, Kanten mit Clustern zu verbinden.",del:"Lösche Auswahl",deleteClusterError:"Cluster können nicht gelöscht werden.",edgeDescription:"Klicke auf einen Knoten und ziehe die Kante zu einem anderen Knoten, um diese zu verbinden.",edit:"Editieren",editClusterError:"Cluster können nicht editiert werden.",editEdge:"Kante editieren",editEdgeDescription:"Klicke auf die Verbindungspunkte und ziehe diese auf einen Knoten, um sie zu verbinden.",editNode:"Knoten editieren"},en:{addDescription:"Click in an empty space to place a new node.",addEdge:"Add Edge",addNode:"Add Node",back:"Back",close:"Close",createEdgeError:"Cannot link edges to a cluster.",del:"Delete selected",deleteClusterError:"Clusters cannot be deleted.",edgeDescription:"Click on a node and drag the edge to another node to connect them.",edit:"Edit",editClusterError:"Clusters cannot be edited.",editEdge:"Edit Edge",editEdgeDescription:"Click on the control points and drag them to a node to connect to it.",editNode:"Edit Node"},es:{addDescription:"Haga clic en un lugar vacío para colocar un nuevo nodo.",addEdge:"Añadir arista",addNode:"Añadir nodo",back:"Atrás",close:"Cerrar",createEdgeError:"No se puede conectar una arista a un grupo.",del:"Eliminar selección",deleteClusterError:"No es posible eliminar grupos.",edgeDescription:"Haga clic en un nodo y arrastre la arista hacia otro nodo para conectarlos.",edit:"Editar",editClusterError:"No es posible editar grupos.",editEdge:"Editar arista",editEdgeDescription:"Haga clic en un punto de control y arrastrelo a un nodo para conectarlo.",editNode:"Editar nodo"},fr:{addDescription:"Cliquez dans un endroit vide pour placer un nœud.",addEdge:"Ajouter un lien",addNode:"Ajouter un nœud",back:"Retour",close:"Fermer",createEdgeError:"Impossible de créer un lien vers un cluster.",del:"Effacer la sélection",deleteClusterError:"Les clusters ne peuvent pas être effacés.",edgeDescription:"Cliquez sur un nœud et glissez le lien vers un autre nœud pour les connecter.",edit:"Éditer",editClusterError:"Les clusters ne peuvent pas être édités.",editEdge:"Éditer le lien",editEdgeDescription:"Cliquez sur les points de contrôle et glissez-les pour connecter un nœud.",editNode:"Éditer le nœud"},it:{addDescription:"Clicca per aggiungere un nuovo nodo",addEdge:"Aggiungi un vertice",addNode:"Aggiungi un nodo",back:"Indietro",close:"Chiudere",createEdgeError:"Non si possono collegare vertici ad un cluster",del:"Cancella la selezione",deleteClusterError:"I cluster non possono essere cancellati",edgeDescription:"Clicca su un nodo e trascinalo ad un altro nodo per connetterli.",edit:"Modifica",editClusterError:"I clusters non possono essere modificati.",editEdge:"Modifica il vertice",editEdgeDescription:"Clicca sui Punti di controllo e trascinali ad un nodo per connetterli.",editNode:"Modifica il nodo"},nl:{addDescription:"Klik op een leeg gebied om een nieuwe node te maken.",addEdge:"Link toevoegen",addNode:"Node toevoegen",back:"Terug",close:"Sluiten",createEdgeError:"Kan geen link maken naar een cluster.",del:"Selectie verwijderen",deleteClusterError:"Clusters kunnen niet worden verwijderd.",edgeDescription:"Klik op een node en sleep de link naar een andere node om ze te verbinden.",edit:"Wijzigen",editClusterError:"Clusters kunnen niet worden aangepast.",editEdge:"Link wijzigen",editEdgeDescription:"Klik op de verbindingspunten en sleep ze naar een node om daarmee te verbinden.",editNode:"Node wijzigen"},pt:{addDescription:"Clique em um espaço em branco para adicionar um novo nó",addEdge:"Adicionar aresta",addNode:"Adicionar nó",back:"Voltar",close:"Fechar",createEdgeError:"Não foi possível linkar arestas a um cluster.",del:"Remover selecionado",deleteClusterError:"Clusters não puderam ser removidos.",edgeDescription:"Clique em um nó e arraste a aresta até outro nó para conectá-los",edit:"Editar",editClusterError:"Clusters não puderam ser editados.",editEdge:"Editar aresta",editEdgeDescription:"Clique nos pontos de controle e os arraste para um nó para conectá-los",editNode:"Editar nó"},ru:{addDescription:"Кликните в свободное место, чтобы добавить новый узел.",addEdge:"Добавить ребро",addNode:"Добавить узел",back:"Назад",close:"Закрывать",createEdgeError:"Невозможно соединить ребра в кластер.",del:"Удалить выбранное",deleteClusterError:"Кластеры не могут быть удалены",edgeDescription:"Кликните на узел и протяните ребро к другому узлу, чтобы соединить их.",edit:"Редактировать",editClusterError:"Кластеры недоступны для редактирования.",editEdge:"Редактировать ребро",editEdgeDescription:"Кликните на контрольные точки и перетащите их в узел, чтобы подключиться к нему.",editNode:"Редактировать узел"},uk:{addDescription:"Kлікніть на вільне місце, щоб додати новий вузол.",addEdge:"Додати край",addNode:"Додати вузол",back:"Назад",close:"Закрити",createEdgeError:"Не можливо об'єднати краї в групу.",del:"Видалити обране",deleteClusterError:"Групи не можуть бути видалені.",edgeDescription:"Клікніть на вузол і перетягніть край до іншого вузла, щоб їх з'єднати.",edit:"Редагувати",editClusterError:"Групи недоступні для редагування.",editEdge:"Редагувати край",editEdgeDescription:"Клікніть на контрольні точки і перетягніть їх у вузол, щоб підключитися до нього.",editNode:"Редагувати вузол"}});function Nf(){return Ef?xf:(Ef=1,jl(),xf=Tg().setInterval)}var kf=C(Tf?Of:(Tf=1,Of=Nf()));function Rf(g,A){A.inputHandler=function(g){g.isFirst&&A(g)},g.on("hammer.input",A.inputHandler)}function Pf(g,A){return A.inputHandler=function(g){g.isFinal&&A(g)},g.on("hammer.input",A.inputHandler)}class Mf{constructor(g){this.body=g,this.pixelRatio=1,this.cameraState={},this.initialized=!1,this.canvasViewCenter={},this._cleanupCallbacks=[],this.options={},this.defaultOptions={autoResize:!0,height:"100%",width:"100%"},_t(this.options,this.defaultOptions),this.bindEventListeners()}bindEventListeners(){var g;this.body.emitter.once("resize",g=>{0!==g.width&&(this.body.view.translation.x=.5*g.width),0!==g.height&&(this.body.view.translation.y=.5*g.height)}),this.body.emitter.on("setSize",IC(g=this.setSize).call(g,this)),this.body.emitter.on("destroy",()=>{this.hammerFrame.destroy(),this.hammer.destroy(),this._cleanUp()})}setOptions(g){if(void 0!==g){op(["width","height","autoResize"],this.options,g)}if(this._cleanUp(),!0===this.options.autoResize){var A;if(window.ResizeObserver){const g=new ResizeObserver(()=>{!0===this.setSize()&&this.body.emitter.emit("_requestRedraw")}),A=this.frame;g.observe(A),this._cleanupCallbacks.push(()=>{g.unobserve(A)})}else{const g=kf(()=>{!0===this.setSize()&&this.body.emitter.emit("_requestRedraw")},1e3);this._cleanupCallbacks.push(()=>{clearInterval(g)})}const g=IC(A=this._onResize).call(A,this);window.addEventListener("resize",g),this._cleanupCallbacks.push(()=>{window.removeEventListener("resize",g)})}}_cleanUp(){var g,A;pa(g=Va(A=this._cleanupCallbacks).call(A,0).toReversed()).call(g,g=>{try{g()}catch(g){console.error(g)}})}_onResize(){this.setSize(),this.body.emitter.emit("_redraw")}_getCameraState(){let g=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.pixelRatio;!0===this.initialized&&(this.cameraState.previousWidth=this.frame.canvas.width/g,this.cameraState.previousHeight=this.frame.canvas.height/g,this.cameraState.scale=this.body.view.scale,this.cameraState.position=this.DOMtoCanvas({x:.5*this.frame.canvas.width/g,y:.5*this.frame.canvas.height/g}))}_setCameraState(){if(void 0!==this.cameraState.scale&&0!==this.frame.canvas.clientWidth&&0!==this.frame.canvas.clientHeight&&0!==this.pixelRatio&&this.cameraState.previousWidth>0&&this.cameraState.previousHeight>0){const g=this.frame.canvas.width/this.pixelRatio/this.cameraState.previousWidth,A=this.frame.canvas.height/this.pixelRatio/this.cameraState.previousHeight;let t=this.cameraState.scale;1!=g&&1!=A?t=.5*this.cameraState.scale*(g+A):1!=g?t=this.cameraState.scale*g:1!=A&&(t=this.cameraState.scale*A),this.body.view.scale=t;const C=this.DOMtoCanvas({x:.5*this.frame.canvas.clientWidth,y:.5*this.frame.canvas.clientHeight}),I={x:C.x-this.cameraState.position.x,y:C.y-this.cameraState.position.y};this.body.view.translation.x+=I.x*this.body.view.scale,this.body.view.translation.y+=I.y*this.body.view.scale}}_prepareValue(g){if("number"==typeof g)return g+"px";if("string"==typeof g){if(-1!==Jh(g).call(g,"%")||-1!==Jh(g).call(g,"px"))return g;if(-1===Jh(g).call(g,"%"))return g+"px"}throw new Error("Could not use the value supplied for width or height:"+g)}_create(){for(;this.body.container.hasChildNodes();)this.body.container.removeChild(this.body.container.firstChild);if(this.frame=document.createElement("div"),this.frame.className="vis-network",this.frame.style.position="relative",this.frame.style.overflow="hidden",this.frame.tabIndex=0,this.frame.canvas=document.createElement("canvas"),this.frame.canvas.style.position="relative",this.frame.appendChild(this.frame.canvas),this.frame.canvas.getContext)this._setPixelRatio(),this.setTransform();else{const g=document.createElement("DIV");g.style.color="red",g.style.fontWeight="bold",g.style.padding="10px",g.innerText="Error: your browser does not support HTML canvas",this.frame.canvas.appendChild(g)}this.body.container.appendChild(this.frame),this.body.view.scale=1,this.body.view.translation={x:.5*this.frame.canvas.clientWidth,y:.5*this.frame.canvas.clientHeight},this._bindHammer()}_bindHammer(){void 0!==this.hammer&&this.hammer.destroy(),this.drag={},this.pinch={},this.hammer=new Bp(this.frame.canvas),this.hammer.get("pinch").set({enable:!0}),this.hammer.get("pan").set({threshold:5,direction:Bp.DIRECTION_ALL}),Rf(this.hammer,g=>{this.body.eventListeners.onTouch(g)}),this.hammer.on("tap",g=>{this.body.eventListeners.onTap(g)}),this.hammer.on("doubletap",g=>{this.body.eventListeners.onDoubleTap(g)}),this.hammer.on("press",g=>{this.body.eventListeners.onHold(g)}),this.hammer.on("panstart",g=>{this.body.eventListeners.onDragStart(g)}),this.hammer.on("panmove",g=>{this.body.eventListeners.onDrag(g)}),this.hammer.on("panend",g=>{this.body.eventListeners.onDragEnd(g)}),this.hammer.on("pinch",g=>{this.body.eventListeners.onPinch(g)}),this.frame.canvas.addEventListener("wheel",g=>{this.body.eventListeners.onMouseWheel(g)}),this.frame.canvas.addEventListener("mousemove",g=>{this.body.eventListeners.onMouseMove(g)}),this.frame.canvas.addEventListener("contextmenu",g=>{this.body.eventListeners.onContext(g)}),this.hammerFrame=new Bp(this.frame),Pf(this.hammerFrame,g=>{this.body.eventListeners.onRelease(g)})}setSize(){let g=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.options.width,A=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.options.height;g=this._prepareValue(g),A=this._prepareValue(A);let t=!1;const C=this.frame.canvas.width,I=this.frame.canvas.height,e=this.pixelRatio;if(this._setPixelRatio(),g!=this.options.width||A!=this.options.height||this.frame.style.width!=g||this.frame.style.height!=A)this._getCameraState(e),this.frame.style.width=g,this.frame.style.height=A,this.frame.canvas.style.width="100%",this.frame.canvas.style.height="100%",this.frame.canvas.width=Math.round(this.frame.canvas.clientWidth*this.pixelRatio),this.frame.canvas.height=Math.round(this.frame.canvas.clientHeight*this.pixelRatio),this.options.width=g,this.options.height=A,this.canvasViewCenter={x:.5*this.frame.clientWidth,y:.5*this.frame.clientHeight},t=!0;else{const g=Math.round(this.frame.canvas.clientWidth*this.pixelRatio),A=Math.round(this.frame.canvas.clientHeight*this.pixelRatio);this.frame.canvas.width===g&&this.frame.canvas.height===A||this._getCameraState(e),this.frame.canvas.width!==g&&(this.frame.canvas.width=g,t=!0),this.frame.canvas.height!==A&&(this.frame.canvas.height=A,t=!0)}return!0===t&&(this.body.emitter.emit("resize",{width:Math.round(this.frame.canvas.width/this.pixelRatio),height:Math.round(this.frame.canvas.height/this.pixelRatio),oldWidth:Math.round(C/this.pixelRatio),oldHeight:Math.round(I/this.pixelRatio)}),this._setCameraState()),this.initialized=!0,t}getContext(){return this.frame.canvas.getContext("2d")}_determinePixelRatio(){const g=this.getContext();if(void 0===g)throw new Error("Could not get canvax context");let A=1;"undefined"!=typeof window&&(A=window.devicePixelRatio||1);return A/(g.webkitBackingStorePixelRatio||g.mozBackingStorePixelRatio||g.msBackingStorePixelRatio||g.oBackingStorePixelRatio||g.backingStorePixelRatio||1)}_setPixelRatio(){this.pixelRatio=this._determinePixelRatio()}setTransform(){const g=this.getContext();if(void 0===g)throw new Error("Could not get canvax context");g.setTransform(this.pixelRatio,0,0,this.pixelRatio,0,0)}_XconvertDOMtoCanvas(g){return(g-this.body.view.translation.x)/this.body.view.scale}_XconvertCanvasToDOM(g){return g*this.body.view.scale+this.body.view.translation.x}_YconvertDOMtoCanvas(g){return(g-this.body.view.translation.y)/this.body.view.scale}_YconvertCanvasToDOM(g){return g*this.body.view.scale+this.body.view.translation.y}canvasToDOM(g){return{x:this._XconvertCanvasToDOM(g.x),y:this._YconvertCanvasToDOM(g.y)}}DOMtoCanvas(g){return{x:this._XconvertDOMtoCanvas(g.x),y:this._YconvertDOMtoCanvas(g.y)}}}class zf{constructor(g,A){this.body=g,this.canvas=A,this.redrawRequested=!1,this.requestAnimationFrameRequestId=void 0,this.renderingActive=!1,this.renderRequests=0,this.allowRedraw=!0,this.dragging=!1,this.zooming=!1,this.options={},this.defaultOptions={hideEdgesOnDrag:!1,hideEdgesOnZoom:!1,hideNodesOnDrag:!1},_t(this.options,this.defaultOptions),this.bindEventListeners()}bindEventListeners(){var g;this.body.emitter.on("dragStart",()=>{this.dragging=!0}),this.body.emitter.on("dragEnd",()=>{this.dragging=!1}),this.body.emitter.on("zoom",()=>{this.zooming=!0,window.clearTimeout(this.zoomTimeoutId),this.zoomTimeoutId=$l(()=>{var g;this.zooming=!1,IC(g=this._requestRedraw).call(g,this)()},250)}),this.body.emitter.on("_resizeNodes",()=>{this._resizeNodes()}),this.body.emitter.on("_redraw",()=>{!1===this.renderingActive&&this._redraw()}),this.body.emitter.on("_blockRedraw",()=>{this.allowRedraw=!1}),this.body.emitter.on("_allowRedraw",()=>{this.allowRedraw=!0,this.redrawRequested=!1}),this.body.emitter.on("_requestRedraw",IC(g=this._requestRedraw).call(g,this)),this.body.emitter.on("_startRendering",()=>{this.renderRequests+=1,this.renderingActive=!0,this._startRendering()}),this.body.emitter.on("_stopRendering",()=>{this.renderRequests-=1,this.renderingActive=this.renderRequests>0,this.requestAnimationFrameRequestId=void 0}),this.body.emitter.on("destroy",()=>{this.renderRequests=0,this.allowRedraw=!1,this.renderingActive=!1,window.cancelAnimationFrame(this.requestAnimationFrameRequestId),this.body.emitter.off()})}setOptions(g){if(void 0!==g){op(["hideEdgesOnDrag","hideEdgesOnZoom","hideNodesOnDrag"],this.options,g)}}_startRendering(){var g;!0===this.renderingActive&&(void 0===this.requestAnimationFrameRequestId&&(this.requestAnimationFrameRequestId=window.requestAnimationFrame(IC(g=this._renderStep).call(g,this),this.simulationInterval)))}_renderStep(){!0===this.renderingActive&&(this.requestAnimationFrameRequestId=void 0,this._startRendering(),this._redraw())}redraw(){this.body.emitter.emit("setSize"),this._redraw()}_requestRedraw(){!0!==this.redrawRequested&&!1===this.renderingActive&&!0===this.allowRedraw&&(this.redrawRequested=!0,window.requestAnimationFrame(()=>{this._redraw(!1)}))}_redraw(){let g=arguments.length>0&&void 0!==arguments[0]&&arguments[0];if(!0===this.allowRedraw){this.body.emitter.emit("initRedraw"),this.redrawRequested=!1;const A={drawExternalLabels:null};0!==this.canvas.frame.canvas.width&&0!==this.canvas.frame.canvas.height||this.canvas.setSize(),this.canvas.setTransform();const t=this.canvas.getContext(),C=this.canvas.frame.canvas.clientWidth,I=this.canvas.frame.canvas.clientHeight;if(t.clearRect(0,0,C,I),0===this.canvas.frame.clientWidth)return;if(t.save(),t.translate(this.body.view.translation.x,this.body.view.translation.y),t.scale(this.body.view.scale,this.body.view.scale),t.beginPath(),this.body.emitter.emit("beforeDrawing",t),t.closePath(),!1===g&&(!1===this.dragging||!0===this.dragging&&!1===this.options.hideEdgesOnDrag)&&(!1===this.zooming||!0===this.zooming&&!1===this.options.hideEdgesOnZoom)&&this._drawEdges(t),!1===this.dragging||!0===this.dragging&&!1===this.options.hideNodesOnDrag){const C=this._drawNodes(t,g).drawExternalLabels;A.drawExternalLabels=C}!1===g&&(!1===this.dragging||!0===this.dragging&&!1===this.options.hideEdgesOnDrag)&&(!1===this.zooming||!0===this.zooming&&!1===this.options.hideEdgesOnZoom)&&this._drawArrows(t),null!=A.drawExternalLabels&&A.drawExternalLabels(),!1===g&&this._drawSelectionBox(t),t.beginPath(),this.body.emitter.emit("afterDrawing",t),t.closePath(),t.restore(),!0===g&&t.clearRect(0,0,C,I)}}_resizeNodes(){this.canvas.setTransform();const g=this.canvas.getContext();g.save(),g.translate(this.body.view.translation.x,this.body.view.translation.y),g.scale(this.body.view.scale,this.body.view.scale);const A=this.body.nodes;let t;for(const C in A)Object.prototype.hasOwnProperty.call(A,C)&&(t=A[C],t.resize(g),t.updateBoundingBox(g,t.selected));g.restore()}_drawNodes(g){let A=arguments.length>1&&void 0!==arguments[1]&&arguments[1];const t=this.body.nodes,C=this.body.nodeIndices;let I;const e=[],i=[],o=this.canvas.DOMtoCanvas({x:-20,y:-20}),n=this.canvas.DOMtoCanvas({x:this.canvas.frame.canvas.clientWidth+20,y:this.canvas.frame.canvas.clientHeight+20}),s={top:o.y,left:o.x,bottom:n.y,right:n.x},r=[];for(let o=0;o{for(const g of r)g()}}}_drawEdges(g){const A=this.body.edges,t=this.body.edgeIndices;for(let C=0;C= 16");return A[6]=15&A[6]|64,A[8]=63&A[8]|128,function(g,A=0){return(Bf[g[A+0]]+Bf[g[A+1]]+Bf[g[A+2]]+Bf[g[A+3]]+"-"+Bf[g[A+4]]+Bf[g[A+5]]+"-"+Bf[g[A+6]]+Bf[g[A+7]]+"-"+Bf[g[A+8]]+Bf[g[A+9]]+"-"+Bf[g[A+10]]+Bf[g[A+11]]+Bf[g[A+12]]+Bf[g[A+13]]+Bf[g[A+14]]+Bf[g[A+15]]).toLowerCase()}(A)}(g)}class Ff{static getRange(g){let A,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[],C=1e9,I=-1e9,e=1e9,i=-1e9;if(t.length>0)for(let o=0;oA.shape.boundingBox.left&&(e=A.shape.boundingBox.left),iA.shape.boundingBox.top&&(C=A.shape.boundingBox.top),I1&&void 0!==arguments[1]?arguments[1]:[],C=1e9,I=-1e9,e=1e9,i=-1e9;if(t.length>0)for(let o=0;oA.x&&(e=A.x),iA.y&&(C=A.y),Ia;)void 0!==(i=n(o,A=s[a++]))&&e(r,A,i);return r}})}(),Pb=Tg().Object.getOwnPropertyDescriptors)}function Lb(){return Bb?zb:(Bb=1,zb=jb())}var Vb,Yb,Wb,Qb,Ub,Kb,Hb=C(Sb?Zb:(Sb=1,Zb=Lb())),Xb={exports:{}},_b={};function Jb(){if(Vb)return _b;Vb=1;var g=st(),A=z(),t=TI().f;return g({target:"Object",stat:!0,forced:Object.defineProperties!==t,sham:!A},{defineProperties:t}),_b}function qb(){if(Yb)return Xb.exports;Yb=1,Jb();var g=Tg().Object,A=Xb.exports=function(A,t){return g.defineProperties(A,t)};return g.defineProperties.sham&&(A.sham=!0),Xb.exports}function $b(){return Qb?Wb:(Qb=1,Wb=qb())}var gm,Am,tm=C(Kb?Ub:(Kb=1,Ub=$b()));var Cm,Im,em,im,om,nm,sm,rm=C(Am?gm:(Am=1,gm=Ov())),am={};function dm(){return em?Im:(em=1,function(){if(Cm)return am;Cm=1;var g=st(),A=Math.hypot,t=Math.abs,C=Math.sqrt;g({target:"Math",stat:!0,arity:2,forced:!!A&&A(1/0,NaN)!==1/0},{hypot:function(g,A){for(var I,e,i=0,o=0,n=arguments.length,s=0;o0?(e=I/s)*e:I;return s===1/0?1/0:s*C(i)}})}(),Im=Tg().Math.hypot)}function hm(){return om?im:(om=1,im=dm())}var lm=C(sm?nm:(sm=1,nm=hm()));function cm(g,A){const t=["node","edge","label"];let C=!0;const I=Op(A,"chosen");if("boolean"==typeof I)C=I;else if("object"==typeof I){if(-1===Jh(t).call(t,g))throw new Error("choosify: subOption '"+g+"' should be one of '"+t.join("', '")+"'");const I=Op(A,["chosen",g]);"boolean"!=typeof I&&"function"!=typeof I||(C=I)}return C}function um(g,A,t){if(g.width<=0||g.height<=0)return!1;if(void 0!==t){const g={x:A.x-t.x,y:A.y-t.y};if(0!==t.angle){const C=-t.angle;A={x:Math.cos(C)*g.x-Math.sin(C)*g.y,y:Math.sin(C)*g.x+Math.cos(C)*g.y}}else A=g}const C=g.x+g.width,I=g.y+g.width;return g.leftA.x&&g.topA.y}function pm(g){return"string"==typeof g&&""!==g}function fm(g,A,t,C){let I=C.x,e=C.y;if("function"==typeof C.distanceToBorder){const t=C.distanceToBorder(g,A),i=Math.sin(A)*t,o=Math.cos(A)*t;o===t?(I+=t,e=C.y):i===t?(I=C.x,e-=t):(I+=o,e-=i)}else C.shape.width>C.shape.height?(I=C.x+.5*C.shape.width,e=C.y-t):(I=C.x+t,e=C.y-.5*C.shape.height);return{x:I,y:e}}class vm{static transform(g,A){cr(g)||(g=[g]);const t=A.point.x,C=A.point.y,I=A.angle,e=A.length;for(let A=0;A4&&void 0!==arguments[4]?arguments[4]:this.getViaNode();g.strokeStyle=this.getColor(g,A),g.lineWidth=A.width,!1!==A.dashes?this._drawDashedLine(g,A,I):this._drawLine(g,A,I)}_drawLine(g,A,t,C,I){if(this.from!=this.to)this._line(g,A,t,C,I);else{const t=js(this._getCircleData(g),3),C=t[0],I=t[1],e=t[2];this._circle(g,A,C,I,e)}}_drawDashedLine(g,A,t,C,I){g.lineCap="round";const e=cr(A.dashes)?A.dashes:[5,5];if(void 0!==g.setLineDash){if(g.save(),g.setLineDash(e),g.lineDashOffset=0,this.from!=this.to)this._line(g,A,t);else{const t=js(this._getCircleData(g),3),C=t[0],I=t[1],e=t[2];this._circle(g,A,C,I,e)}g.setLineDash([0]),g.lineDashOffset=0,g.restore()}else{if(this.from!=this.to)sC(g,this.from.x,this.from.y,this.to.x,this.to.y,e);else{const t=js(this._getCircleData(g),3),C=t[0],I=t[1],e=t[2];this._circle(g,A,C,I,e)}this.enableShadow(g,A),g.stroke(),this.disableShadow(g,A)}}findBorderPosition(g,A,t){return this.from!=this.to?this._findBorderPosition(g,A,t):this._findBorderPositionCircle(g,A,t)}findBorderPositions(g){if(this.from!=this.to)return{from:this._findBorderPosition(this.from,g),to:this._findBorderPosition(this.to,g)};{var A;const t=js($s(A=this._getCircleData(g)).call(A,0,2),2),C=t[0],I=t[1];return{from:this._findBorderPositionCircle(this.from,g,{x:C,y:I,low:.25,high:.6,direction:-1}),to:this._findBorderPositionCircle(this.from,g,{x:C,y:I,low:.6,high:.8,direction:1})}}}_getCircleData(g){const A=this.options.selfReference.size;void 0!==g&&void 0===this.from.shape.width&&this.from.shape.resize(g);const t=fm(g,this.options.selfReference.angle,A,this.from);return[t.x,t.y,A]}_pointOnCircle(g,A,t,C){const I=2*C*Math.PI;return{x:g+t*Math.cos(I),y:A-t*Math.sin(I)}}_findBorderPositionCircle(g,A,t){const C=t.x,I=t.y;let e=t.low,i=t.high;const o=t.direction,n=this.options.selfReference.size;let s,r=.5*(e+i),a=0;!0===this.options.arrowStrikethrough&&(-1===o?a=this.options.endPointOffset.from:1===o&&(a=this.options.endPointOffset.to));let d=0;do{r=.5*(e+i),s=this._pointOnCircle(C,I,n,r);const t=Math.atan2(g.y-s.y,g.x-s.x),h=g.distanceToBorder(A,t)+a-Math.sqrt(Math.pow(s.x-g.x,2)+Math.pow(s.y-g.y,2));if(Math.abs(h)<.05)break;h>0?o>0?e=r:i=r:o>0?i=r:e=r,++d}while(e<=i&&d<10);return zm(zm({},s),{},{t:r})}getLineWidth(g,A){return!0===g?Math.max(this.selectionWidth,.3/this._body.view.scale):!0===A?Math.max(this.hoverWidth,.3/this._body.view.scale):Math.max(this.options.width,.3/this._body.view.scale)}getColor(g,A){if(!1!==A.inheritsColor){if("both"===A.inheritsColor&&this.from.id!==this.to.id){const t=g.createLinearGradient(this.from.x,this.from.y,this.to.x,this.to.y);let C=this.from.options.color.highlight.border,I=this.to.options.color.highlight.border;return!1===this.from.selected&&!1===this.to.selected?(C=cp(this.from.options.color.border,A.opacity),I=cp(this.to.options.color.border,A.opacity)):!0===this.from.selected&&!1===this.to.selected?I=this.to.options.color.border:!1===this.from.selected&&!0===this.to.selected&&(C=this.from.options.color.border),t.addColorStop(0,C),t.addColorStop(1,I),t}return"to"===A.inheritsColor?cp(this.to.options.color.border,A.opacity):cp(this.from.options.color.border,A.opacity)}return cp(A.color,A.opacity)}_circle(g,A,t,C,I){this.enableShadow(g,A);let e=0,i=2*Math.PI;if(!this.options.selfReference.renderBehindTheNode){const A=this.options.selfReference.angle,I=this.options.selfReference.angle+Math.PI,o=this._findBorderPositionCircle(this.from,g,{x:t,y:C,low:A,high:I,direction:-1}),n=this._findBorderPositionCircle(this.from,g,{x:t,y:C,low:A,high:I,direction:1});e=Math.atan2(o.y-C,o.x-t),i=Math.atan2(n.y-C,n.x-t)}g.beginPath(),g.arc(t,C,I,e,i,!1),g.stroke(),this.disableShadow(g,A)}getDistanceToEdge(g,A,t,C,I,e){if(this.from!=this.to)return this._getDistanceToEdge(g,A,t,C,I,e);{const g=js(this._getCircleData(void 0),3),A=g[0],t=g[1],C=g[2],i=A-I,o=t-e;return Math.abs(Math.sqrt(i*i+o*o)-C)}}_getDistanceToLine(g,A,t,C,I,e){const i=t-g,o=C-A;let n=((I-g)*i+(e-A)*o)/(i*i+o*o);n>1?n=1:n<0&&(n=0);const s=g+n*i-I,r=A+n*o-e;return Math.sqrt(s*s+r*r)}getArrowData(g,A,t,C,I,e){let i,o,n,s,r,a,d;const h=e.width;"from"===A?(n=this.from,s=this.to,r=e.fromArrowScale<0,a=Math.abs(e.fromArrowScale),d=e.fromArrowType):"to"===A?(n=this.to,s=this.from,r=e.toArrowScale<0,a=Math.abs(e.toArrowScale),d=e.toArrowType):(n=this.to,s=this.from,r=e.middleArrowScale<0,a=Math.abs(e.middleArrowScale),d=e.middleArrowType);const l=15*a+3*h;if(n!=s){const C=l/lm(n.x-s.x,n.y-s.y);if("middle"!==A)if(!0===this.options.smooth.enabled){const I=this._findBorderPosition(n,g,{via:t}),e=this.getPoint(I.t+C*("from"===A?1:-1),t);i=Math.atan2(I.y-e.y,I.x-e.x),o=I}else i=Math.atan2(n.y-s.y,n.x-s.x),o=this._findBorderPosition(n,g);else{const g=(r?-C:C)/2,A=this.getPoint(.5+g,t),I=this.getPoint(.5-g,t);i=Math.atan2(A.y-I.y,A.x-I.x),o=this.getPoint(.5,t)}}else{const t=js(this._getCircleData(g),3),C=t[0],I=t[1],e=t[2];if("from"===A){const A=this.options.selfReference.angle,t=this.options.selfReference.angle+Math.PI,e=this._findBorderPositionCircle(this.from,g,{x:C,y:I,low:A,high:t,direction:-1});i=-2*e.t*Math.PI+1.5*Math.PI+.1*Math.PI,o=e}else if("to"===A){const A=this.options.selfReference.angle,t=this.options.selfReference.angle+Math.PI,e=this._findBorderPositionCircle(this.from,g,{x:C,y:I,low:A,high:t,direction:1});i=-2*e.t*Math.PI+1.5*Math.PI-1.1*Math.PI,o=e}else{const g=this.options.selfReference.angle/(2*Math.PI);o=this._pointOnCircle(C,I,e,g),i=-2*g*Math.PI+1.5*Math.PI+.1*Math.PI}}return{point:o,core:{x:o.x-.9*l*Math.cos(i),y:o.y-.9*l*Math.sin(i)},angle:i,length:l,type:d}}drawArrowHead(g,A,t,C,I){g.strokeStyle=this.getColor(g,A),g.fillStyle=g.strokeStyle,g.lineWidth=A.width;Pm.draw(g,I)&&(this.enableShadow(g,A),ic(g).call(g),this.disableShadow(g,A))}enableShadow(g,A){!0===A.shadow&&(g.shadowColor=A.shadowColor,g.shadowBlur=A.shadowSize,g.shadowOffsetX=A.shadowX,g.shadowOffsetY=A.shadowY)}disableShadow(g,A){!0===A.shadow&&(g.shadowColor="rgba(0,0,0,0)",g.shadowBlur=0,g.shadowOffsetX=0,g.shadowOffsetY=0)}drawBackground(g,A){if(!1!==A.background){const t={strokeStyle:g.strokeStyle,lineWidth:g.lineWidth,dashes:g.dashes};g.strokeStyle=A.backgroundColor,g.lineWidth=A.backgroundSize,this.setStrokeDashed(g,A.backgroundDashes),g.stroke(),g.strokeStyle=t.strokeStyle,g.lineWidth=t.lineWidth,g.dashes=t.dashes,this.setStrokeDashed(g,A.dashes)}}setStrokeDashed(g,A){if(!1!==A)if(void 0!==g.setLineDash){const t=cr(A)?A:[5,5];g.setLineDash(t)}else console.warn("setLineDash is not supported in this browser. The dashed stroke cannot be used.");else void 0!==g.setLineDash?g.setLineDash([]):console.warn("setLineDash is not supported in this browser. The dashed stroke cannot be used.")}}function Zm(g,A){var t=Fr(g);if(Ob){var C=Ob(g);A&&(C=eh(C).call(C,function(A){return Fb(g,A).enumerable})),t.push.apply(t,C)}return t}function Sm(g){for(var A=1;A2&&void 0!==arguments[2]?arguments[2]:this._getViaCoordinates();let C,I,e=!1,i=1,o=0,n=this.to,s=this.options.endPointOffset?this.options.endPointOffset.to:0;g.id===this.from.id&&(n=this.from,e=!0,s=this.options.endPointOffset?this.options.endPointOffset.from:0),!1===this.options.arrowStrikethrough&&(s=0);let r=0;do{I=.5*(o+i),C=this.getPoint(I,t);const g=Math.atan2(n.y-C.y,n.x-C.x),a=n.distanceToBorder(A,g)+s-Math.sqrt(Math.pow(C.x-n.x,2)+Math.pow(C.y-n.y,2));if(Math.abs(a)<.2)break;a<0?!1===e?o=I:i=I:!1===e?i=I:o=I,++r}while(o<=i&&r<10);return Sm(Sm({},C),{},{t:I})}_getDistanceToBezierEdge(g,A,t,C,I,e,i){let o,n,s,r,a,d=1e9,h=g,l=A;for(n=1;n<10;n++)s=.1*n,r=Math.pow(1-s,2)*g+2*s*(1-s)*i.x+Math.pow(s,2)*t,a=Math.pow(1-s,2)*A+2*s*(1-s)*i.y+Math.pow(s,2)*C,n>0&&(o=this._getDistanceToLine(h,l,r,a,I,e),d=o{this.positionBezierNode()},this._body.emitter.on("_repositionBezierNodes",this._boundFunction)}setOptions(g){super.setOptions(g);let A=!1;this.options.physics!==g.physics&&(A=!0),this.options=g,this.id=this.options.id,this.from=this._body.nodes[this.options.from],this.to=this._body.nodes[this.options.to],this.setupSupportNode(),this.connect(),!0===A&&(this.via.setOptions({physics:this.options.physics}),this.positionBezierNode())}connect(){this.from=this._body.nodes[this.options.from],this.to=this._body.nodes[this.options.to],void 0===this.from||void 0===this.to||!1===this.options.physics||this.from.id===this.to.id?this.via.setOptions({physics:!1}):this.via.setOptions({physics:!0})}cleanup(){return this._body.emitter.off("_repositionBezierNodes",this._boundFunction),void 0!==this.via&&(delete this._body.nodes[this.via.id],this.via=void 0,!0)}setupSupportNode(){if(void 0===this.via){const g="edgeId:"+this.id,A=this._body.functions.createNode({id:g,shape:"circle",physics:!0,hidden:!0});this._body.nodes[g]=A,this.via=A,this.via.parentEdgeId=this.id,this.positionBezierNode()}}positionBezierNode(){void 0!==this.via&&void 0!==this.from&&void 0!==this.to?(this.via.x=.5*(this.from.x+this.to.x),this.via.y=.5*(this.from.y+this.to.y)):void 0!==this.via&&(this.via.x=0,this.via.y=0)}_line(g,A,t){this._bezierCurve(g,A,t)}_getViaCoordinates(){return this.via}getViaNode(){return this.via}getPoint(g){let A=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.via;if(this.from===this.to){const A=js(this._getCircleData(),3),t=A[0],C=A[1],I=A[2],e=2*Math.PI*(1-g);return{x:t+I*Math.sin(e),y:C+I-I*(1-Math.cos(e))}}return{x:Math.pow(1-g,2)*this.fromPoint.x+2*g*(1-g)*A.x+Math.pow(g,2)*this.toPoint.x,y:Math.pow(1-g,2)*this.fromPoint.y+2*g*(1-g)*A.y+Math.pow(g,2)*this.toPoint.y}}_findBorderPosition(g,A){return this._findBorderPositionBezier(g,A,this.via)}_getDistanceToEdge(g,A,t,C,I,e){return this._getDistanceToBezierEdge(g,A,t,C,I,e,this.via)}}class jm extends Fm{_line(g,A,t){this._bezierCurve(g,A,t)}getViaNode(){return this._getViaCoordinates()}_getViaCoordinates(){const g=this.options.smooth.roundness,A=this.options.smooth.type;let t=Math.abs(this.from.x-this.to.x),C=Math.abs(this.from.y-this.to.y);if("discrete"===A||"diagonalCross"===A){let I,e;I=e=t<=C?g*C:g*t,this.from.x>this.to.x&&(I=-I),this.from.y>=this.to.y&&(e=-e);let i=this.from.x+I,o=this.from.y+e;return"discrete"===A&&(t<=C?i=tthis.to.x&&(A=-A),this.from.y>=this.to.y&&(I=-I);let e=this.from.x+A,i=this.from.y+I;return t<=C?e=this.from.x<=this.to.x?this.to.xe?this.to.x:e:i=this.from.y>=this.to.y?this.to.y>i?this.to.y:i:this.to.y2&&void 0!==arguments[2]?arguments[2]:{};return this._findBorderPositionBezier(g,A,t.via)}_getDistanceToEdge(g,A,t,C,I,e){let i=arguments.length>6&&void 0!==arguments[6]?arguments[6]:this._getViaCoordinates();return this._getDistanceToBezierEdge(g,A,t,C,I,e,i)}getPoint(g){let A=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this._getViaCoordinates();const t=g;return{x:Math.pow(1-t,2)*this.fromPoint.x+2*t*(1-t)*A.x+Math.pow(t,2)*this.toPoint.x,y:Math.pow(1-t,2)*this.fromPoint.y+2*t*(1-t)*A.y+Math.pow(t,2)*this.toPoint.y}}}class Lm extends Fm{_getDistanceToBezierEdge2(g,A,t,C,I,e,i,o){let n=1e9,s=g,r=A;const a=[0,0,0,0];for(let d=1;d<10;d++){const h=.1*d;a[0]=Math.pow(1-h,3),a[1]=3*h*Math.pow(1-h,2),a[2]=3*Math.pow(h,2)*(1-h),a[3]=Math.pow(h,3);const l=a[0]*g+a[1]*i.x+a[2]*o.x+a[3]*t,c=a[0]*A+a[1]*i.y+a[2]*o.y+a[3]*C;if(d>0){const g=this._getDistanceToLine(s,r,l,c,I,e);n=gMath.abs(A)||!0===this.options.smooth.forceDirection||"horizontal"===this.options.smooth.forceDirection)&&"vertical"!==this.options.smooth.forceDirection?(C=this.from.y,e=this.to.y,t=this.from.x-i*g,I=this.to.x+i*g):(C=this.from.y-i*A,e=this.to.y+i*A,t=this.from.x,I=this.to.x),[{x:t,y:C},{x:I,y:e}]}getViaNode(){return this._getViaCoordinates()}_findBorderPosition(g,A){return this._findBorderPositionBezier(g,A)}_getDistanceToEdge(g,A,t,C,I,e){let i=js(arguments.length>6&&void 0!==arguments[6]?arguments[6]:this._getViaCoordinates(),2),o=i[0],n=i[1];return this._getDistanceToBezierEdge2(g,A,t,C,I,e,o,n)}getPoint(g){let A=js(arguments.length>1&&void 0!==arguments[1]?arguments[1]:this._getViaCoordinates(),2),t=A[0],C=A[1];const I=g,e=[Math.pow(1-I,3),3*I*Math.pow(1-I,2),3*Math.pow(I,2)*(1-I),Math.pow(I,3)];return{x:e[0]*this.fromPoint.x+e[1]*t.x+e[2]*C.x+e[3]*this.toPoint.x,y:e[0]*this.fromPoint.y+e[1]*t.y+e[2]*C.y+e[3]*this.toPoint.y}}}class Ym extends Bm{_line(g,A){g.beginPath(),g.moveTo(this.fromPoint.x,this.fromPoint.y),g.lineTo(this.toPoint.x,this.toPoint.y),this.enableShadow(g,A),g.stroke(),this.disableShadow(g,A)}getViaNode(){}getPoint(g){return{x:(1-g)*this.fromPoint.x+g*this.toPoint.x,y:(1-g)*this.fromPoint.y+g*this.toPoint.y}}_findBorderPosition(g,A){let t=this.to,C=this.from;g.id===this.from.id&&(t=this.from,C=this.to);const I=Math.atan2(t.y-C.y,t.x-C.x),e=t.x-C.x,i=t.y-C.y,o=Math.sqrt(e*e+i*i),n=(o-g.distanceToBorder(A,I))/o;return{x:(1-n)*C.x+n*t.x,y:(1-n)*C.y+n*t.y,t:0}}_getDistanceToEdge(g,A,t,C,I,e){return this._getDistanceToLine(g,A,t,C,I,e)}}var Wm,Qm,Um,Km,Hm,Xm,_m,Jm;function qm(){return Qm?Wm:(Qm=1,eo(),Wm=gC()("Array","values"))}function $m(){return Km?Um:(Km=1,Um=qm())}function gy(){if(Xm)return Hm;Xm=1,oo();var g=oI(),A=vA(),t=Ng(),C=$m(),I=Array.prototype,e={DOMTokenList:!0,NodeList:!0};return Hm=function(i){var o=i.values;return i===I||t(I,i)&&o===I.values||A(e,g(i))?C:o},Hm}var Ay=C(Jm?_m:(Jm=1,_m=gy()));class ty{constructor(g){this.measureText=g,this.current=0,this.width=0,this.height=0,this.lines=[]}_add(g,A){let t=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"normal";void 0===this.lines[g]&&(this.lines[g]={width:0,height:0,blocks:[]});let C=A;void 0!==A&&""!==A||(C=" ");const I=this.measureText(C,t),e=_t({},Ay(I));e.text=A,e.width=I.width,e.mod=t,void 0!==A&&""!==A||(e.width=0),this.lines[g].blocks.push(e),this.lines[g].width+=e.width}curWidth(){const g=this.lines[this.current];return void 0===g?0:g.width}append(g){let A=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"normal";this._add(this.current,g,A)}newLine(g){let A=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"normal";this._add(this.current,g,A),this.current++}determineLineHeights(){for(let g=0;gg&&(g=C.width),A+=C.height}this.width=g,this.height=A}removeEmptyBlocks(){const g=[];for(let A=0;A"://,""://,""://,"":/<\/b>/,"":/<\/i>/,"":/<\/code>/,"*":/\*/,_:/_/,"`":/`/,afterBold:/[^*]/,afterItal:/[^_]/,afterMono:/[^`]/};class Iy{constructor(g){this.text=g,this.bold=!1,this.ital=!1,this.mono=!1,this.spacing=!1,this.position=0,this.buffer="",this.modStack=[],this.blocks=[]}mod(){return 0===this.modStack.length?"normal":this.modStack[0]}modName(){return 0===this.modStack.length?"normal":"mono"===this.modStack[0]?"mono":this.bold&&this.ital?"boldital":this.bold?"bold":this.ital?"ital":void 0}emitBlock(){this.spacing&&(this.add(" "),this.spacing=!1),this.buffer.length>0&&(this.blocks.push({text:this.buffer,mod:this.modName()}),this.buffer="")}add(g){" "===g&&(this.spacing=!0),this.spacing&&(this.buffer+=" ",this.spacing=!1)," "!=g&&(this.buffer+=g)}parseWS(g){return!!/[ \t]/.test(g)&&(this.mono?this.add(g):this.spacing=!0,!0)}setTag(g){this.emitBlock(),this[g]=!0,this.modStack.unshift(g)}unsetTag(g){this.emitBlock(),this[g]=!1,this.modStack.shift()}parseStartTag(g,A){return!(this.mono||this[g]||!this.match(A))&&(this.setTag(g),!0)}match(g){let A=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];const t=js(this.prepareRegExp(g),2),C=t[0],I=t[1],e=C.test(this.text.substr(this.position,I));return e&&A&&(this.position+=I-1),e}parseEndTag(g,A,t){let C=this.mod()===g;return C="mono"===g?C&&this.mono:C&&!this.mono,!(!C||!this.match(A))&&(void 0!==t?(this.position===this.text.length-1||this.match(t,!1))&&this.unsetTag(g):this.unsetTag(g),!0)}replace(g,A){return!!this.match(g)&&(this.add(A),this.position+=length-1,!0)}prepareRegExp(g){let A,t;if(g instanceof RegExp)t=g,A=1;else{const C=Cy[g];t=void 0!==C?C:new RegExp(g),A=g.length}return[t,A]}}class ey{constructor(g,A,t,C){this.ctx=g,this.parent=A,this.selected=t,this.hover=C;this.lines=new ty((A,I)=>{if(void 0===A)return 0;const e=this.parent.getFormattingValues(g,t,C,I);let i=0;if(""!==A){i=this.ctx.measureText(A).width}return{width:i,values:e}})}process(g){if(!pm(g))return this.lines.finalize();const A=this.parent.fontOptions;g=(g=g.replace(/\r\n/g,"\n")).replace(/\r/g,"\n");const t=String(g).split("\n"),C=t.length;if(A.multi)for(let g=0;g0)for(let g=0;g0)for(let g=0;g{if(/&/.test(g)){return A.replace(A.text,"<","<")||A.replace(A.text,"&","&")||A.add("&"),!0}return!1};for(;A.position")||A.parseStartTag("ital","")||A.parseStartTag("mono","")||A.parseEndTag("bold","
    ")||A.parseEndTag("ital","")||A.parseEndTag("mono",""))||t(g)||A.add(g),A.position++}return A.emitBlock(),A.blocks}splitMarkdownBlocks(g){const A=new Iy(g);let t=!0;const C=g=>!!/\\/.test(g)&&(A.positionthis.parent.fontOptions.maxWdt}getLongestFit(g){let A="",t=0;for(;t1&&void 0!==arguments[1]?arguments[1]:"normal",t=arguments.length>2&&void 0!==arguments[2]&&arguments[2];this.parent.getFormattingValues(this.ctx,this.selected,this.hover,A);let C=(g=(g=g.replace(/^( +)/g,"$1\r")).replace(/([^\r][^ ]*)( +)/g,"$1\r$2\r")).split("\r");for(;C.length>0;){let g=this.getLongestFit(C);if(0===g){const g=C[0],t=this.getLongestFitWord(g);this.lines.newLine($s(g).call(g,0,t),A),C[0]=$s(g).call(g,t)}else{let I=g;" "===C[g-1]?g--:" "===C[I]&&I++;const e=$s(C).call(C,0,g).join("");g==C.length&&t?this.lines.append(e,A):this.lines.newLine(e,A),C=$s(C).call(C,I)}}}}const iy=["bold","ital","boldital","mono"];class oy{constructor(g,A){let t=arguments.length>2&&void 0!==arguments[2]&&arguments[2];this.body=g,this.pointToSelf=!1,this.baseSize=void 0,this.fontOptions={},this.setOptions(A),this.size={top:0,left:0,width:0,height:0,yLine:0},this.isEdgeLabel=t}setOptions(g){if(this.elementOptions=g,this.initFontOptions(g.font),pm(g.label)?this.labelDirty=!0:g.label=void 0,void 0!==g.font&&null!==g.font)if("string"==typeof g.font)this.baseSize=this.fontOptions.size;else if("object"==typeof g.font){const A=g.font.size;void 0!==A&&(this.baseSize=A)}}initFontOptions(g){hp(iy,g=>{this.fontOptions[g]={}}),oy.parseFontString(this.fontOptions,g)?this.fontOptions.vadjust=0:hp(g,(g,A)=>{null!=g&&"object"!=typeof g&&(this.fontOptions[A]=g)})}static parseFontString(g,A){if(!A||"string"!=typeof A)return!1;const t=A.split(" ");return g.size=+t[0].replace("px",""),g.face=t[1],g.color=t[2],!0}constrain(g){const A={constrainWidth:!1,maxWdt:-1,minWdt:-1,constrainHeight:!1,minHgt:-1,valign:"middle"},t=Op(g,"widthConstraint");if("number"==typeof t)A.maxWdt=Number(t),A.minWdt=Number(t);else if("object"==typeof t){const t=Op(g,["widthConstraint","maximum"]);"number"==typeof t&&(A.maxWdt=Number(t));const C=Op(g,["widthConstraint","minimum"]);"number"==typeof C&&(A.minWdt=Number(C))}const C=Op(g,"heightConstraint");if("number"==typeof C)A.minHgt=Number(C);else if("object"==typeof C){const t=Op(g,["heightConstraint","minimum"]);"number"==typeof t&&(A.minHgt=Number(t));const C=Op(g,["heightConstraint","valign"]);"string"==typeof C&&("top"!==C&&"bottom"!==C||(A.valign=C))}return A}update(g,A){this.setOptions(g,!0),this.propagateFonts(A),sp(this.fontOptions,this.constrain(A)),this.fontOptions.chooser=cm("label",A)}adjustSizes(g){const A=g?g.right+g.left:0;this.fontOptions.constrainWidth&&(this.fontOptions.maxWdt-=A,this.fontOptions.minWdt-=A);const t=g?g.top+g.bottom:0;this.fontOptions.constrainHeight&&(this.fontOptions.minHgt-=t)}addFontOptionsToPile(g,A){for(let t=0;t{void 0!==g&&(Object.prototype.hasOwnProperty.call(A,t)||(-1!==Jh(iy).call(iy,t)?A[t]={}:A[t]=g))})}return A}getFontOption(g,A,t){let C;for(let I=0;I{C[A]=g}),C.size=Number(C.size),C.vadjust=Number(C.vadjust)}}draw(g,A,t,C,I){let e=arguments.length>5&&void 0!==arguments[5]?arguments[5]:"middle";if(void 0===this.elementOptions.label)return;let i=this.fontOptions.size*this.body.view.scale;this.elementOptions.label&&i=this.elementOptions.scaling.label.maxVisible&&(i=Number(this.elementOptions.scaling.label.maxVisible)/this.body.view.scale),this.calculateLabelSize(g,C,I,A,t,e),this._drawBackground(g),this._drawText(g,A,this.size.yLine,e,i))}_drawBackground(g){if(void 0!==this.fontOptions.background&&"none"!==this.fontOptions.background){g.fillStyle=this.fontOptions.background;const A=this.getSize();g.fillRect(A.left,A.top,A.width,A.height)}}_drawText(g,A,t){let C=arguments.length>3&&void 0!==arguments[3]?arguments[3]:"middle",I=arguments.length>4?arguments[4]:void 0;var e=js(this._setAlignment(g,A,t,C),2);A=e[0],t=e[1],g.textAlign="left",A-=this.size.width/2,this.fontOptions.valign&&this.size.height>this.size.labelHeight&&("top"===this.fontOptions.valign&&(t-=(this.size.height-this.size.labelHeight)/2),"bottom"===this.fontOptions.valign&&(t+=(this.size.height-this.size.labelHeight)/2));for(let C=0;C0&&(g.lineWidth=o.strokeWidth,g.strokeStyle=r,g.lineJoin="round"),g.fillStyle=s,o.strokeWidth>0&&g.strokeText(o.text,A+C,t+o.vadjust),g.fillText(o.text,A+C,t+o.vadjust),C+=o.width}t+=e.height}}}_setAlignment(g,A,t,C){if(this.isEdgeLabel&&"horizontal"!==this.fontOptions.align&&!1===this.pointToSelf){A=0,t=0;const C=2;"top"===this.fontOptions.align?(g.textBaseline="alphabetic",t-=2*C):"bottom"===this.fontOptions.align?(g.textBaseline="hanging",t+=2*C):g.textBaseline="middle"}else g.textBaseline=C;return[A,t]}_getColor(g,A,t){let C=g||"#000000",I=t||"#ffffff";if(A<=this.elementOptions.scaling.label.drawThreshold){const g=Math.max(0,Math.min(1,1-(this.elementOptions.scaling.label.drawThreshold-A)));C=cp(C,g),I=cp(I,g)}return[C,I]}getTextSize(g){let A=arguments.length>1&&void 0!==arguments[1]&&arguments[1],t=arguments.length>2&&void 0!==arguments[2]&&arguments[2];return this._processLabel(g,A,t),{width:this.size.width,height:this.size.height,lineCount:this.lineCount}}getSize(){let g=this.size.left,A=this.size.top-1;if(this.isEdgeLabel){const t=.5*-this.size.width;switch(this.fontOptions.align){case"middle":g=t,A=.5*-this.size.height;break;case"top":g=t,A=-(this.size.height+2);break;case"bottom":g=t,A=2}}return{left:g,top:A,width:this.size.width,height:this.size.height}}calculateLabelSize(g,A,t){let C=arguments.length>3&&void 0!==arguments[3]?arguments[3]:0,I=arguments.length>4&&void 0!==arguments[4]?arguments[4]:0,e=arguments.length>5&&void 0!==arguments[5]?arguments[5]:"middle";this._processLabel(g,A,t),this.size.left=C-.5*this.size.width,this.size.top=I-.5*this.size.height,this.size.yLine=I+.5*(1-this.lineCount)*this.fontOptions.size,"hanging"===e&&(this.size.top+=.5*this.fontOptions.size,this.size.top+=4,this.size.yLine+=4)}getFormattingValues(g,A,t,C){const I=function(g,A,t){return"normal"===A?"mod"===t?"":g[t]:void 0!==g[A][t]?g[A][t]:g[t]},e={color:I(this.fontOptions,C,"color"),size:I(this.fontOptions,C,"size"),face:I(this.fontOptions,C,"face"),mod:I(this.fontOptions,C,"mod"),vadjust:I(this.fontOptions,C,"vadjust"),strokeWidth:this.fontOptions.strokeWidth,strokeColor:this.fontOptions.strokeColor};(A||t)&&("normal"===C&&!0===this.fontOptions.chooser&&this.elementOptions.labelHighlightBold?e.mod="bold":"function"==typeof this.fontOptions.chooser&&this.fontOptions.chooser(e,this.elementOptions.id,A,t));let i="";return void 0!==e.mod&&""!==e.mod&&(i+=e.mod+" "),i+=e.size+"px "+e.face,g.font=i.replace(/"/g,""),e.font=g.font,e.height=e.size,e}differentState(g,A){return g!==this.selectedState||A!==this.hoverState}_processLabelText(g,A,t,C){return new ey(g,this,A,t).process(C)}_processLabel(g,A,t){if(!1===this.labelDirty&&!this.differentState(A,t))return;const C=this._processLabelText(g,A,t,this.elementOptions.label);this.fontOptions.minWdt>0&&C.width0&&C.height2&&void 0!==arguments[2]&&arguments[2],C=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{},I=arguments.length>4&&void 0!==arguments[4]&&arguments[4];if(op(["endPointOffset","arrowStrikethrough","id","from","hidden","hoverWidth","labelHighlightBold","length","line","opacity","physics","scaling","selectionWidth","selfReferenceSize","selfReference","to","title","value","width","font","chosen","widthConstraint"],g,A,t),void 0!==A.endPointOffset&&void 0!==A.endPointOffset.from&&(mv(A.endPointOffset.from)?g.endPointOffset.from=A.endPointOffset.from:(g.endPointOffset.from=void 0!==C.endPointOffset.from?C.endPointOffset.from:0,console.error("endPointOffset.from is not a valid number"))),void 0!==A.endPointOffset&&void 0!==A.endPointOffset.to&&(mv(A.endPointOffset.to)?g.endPointOffset.to=A.endPointOffset.to:(g.endPointOffset.to=void 0!==C.endPointOffset.to?C.endPointOffset.to:0,console.error("endPointOffset.to is not a valid number"))),pm(A.label)?g.label=A.label:pm(g.label)||(g.label=void 0),xp(g,A,"smooth",C),xp(g,A,"shadow",C),xp(g,A,"background",C),void 0!==A.dashes&&null!==A.dashes?g.dashes=A.dashes:!0===t&&null===A.dashes&&(g.dashes=hl(C.dashes)),void 0!==A.scaling&&null!==A.scaling?(void 0!==A.scaling.min&&(g.scaling.min=A.scaling.min),void 0!==A.scaling.max&&(g.scaling.max=A.scaling.max),xp(g.scaling,A.scaling,"label",C.scaling)):!0===t&&null===A.scaling&&(g.scaling=hl(C.scaling)),void 0!==A.arrows&&null!==A.arrows)if("string"==typeof A.arrows){const t=A.arrows.toLowerCase();g.arrows.to.enabled=-1!=Jh(t).call(t,"to"),g.arrows.middle.enabled=-1!=Jh(t).call(t,"middle"),g.arrows.from.enabled=-1!=Jh(t).call(t,"from")}else{if("object"!=typeof A.arrows)throw new Error("The arrow newOptions can only be an object or a string. Refer to the documentation. You used:"+Tl(A.arrows));xp(g.arrows,A.arrows,"to",C.arrows),xp(g.arrows,A.arrows,"middle",C.arrows),xp(g.arrows,A.arrows,"from",C.arrows)}else!0===t&&null===A.arrows&&(g.arrows=hl(C.arrows));if(void 0!==A.color&&null!==A.color){const e=Cp(A.color)?{color:A.color,highlight:A.color,hover:A.color,inherit:!1,opacity:1}:A.color,i=g.color;if(I)sp(i,C.color,!1,t);else for(const g in i)Object.prototype.hasOwnProperty.call(i,g)&&delete i[g];if(Cp(i))i.color=i,i.highlight=i,i.hover=i,i.inherit=!1,void 0===e.opacity&&(i.opacity=1);else{let g=!1;void 0!==e.color&&(i.color=e.color,g=!0),void 0!==e.highlight&&(i.highlight=e.highlight,g=!0),void 0!==e.hover&&(i.hover=e.hover,g=!0),void 0!==e.inherit&&(i.inherit=e.inherit),void 0!==e.opacity&&(i.opacity=Math.min(1,Math.max(0,e.opacity))),!0===g?i.inherit=!1:void 0===i.inherit&&(i.inherit="from")}}else!0===t&&null===A.color&&(g.color=wp(C.color));!0===t&&null===A.font&&(g.font=wp(C.font)),Object.prototype.hasOwnProperty.call(A,"selfReferenceSize")&&(console.warn("The selfReferenceSize property has been deprecated. Please use selfReference property instead. The selfReference can be set like thise selfReference:{size:30, angle:Math.PI / 4}"),g.selfReference.size=A.selfReferenceSize)}getFormattingValues(){const g=!0===this.options.arrows.to||!0===this.options.arrows.to.enabled,A=!0===this.options.arrows.from||!0===this.options.arrows.from.enabled,t=!0===this.options.arrows.middle||!0===this.options.arrows.middle.enabled,C=this.options.color.inherit,I={toArrow:g,toArrowScale:this.options.arrows.to.scaleFactor,toArrowType:this.options.arrows.to.type,toArrowSrc:this.options.arrows.to.src,toArrowImageWidth:this.options.arrows.to.imageWidth,toArrowImageHeight:this.options.arrows.to.imageHeight,middleArrow:t,middleArrowScale:this.options.arrows.middle.scaleFactor,middleArrowType:this.options.arrows.middle.type,middleArrowSrc:this.options.arrows.middle.src,middleArrowImageWidth:this.options.arrows.middle.imageWidth,middleArrowImageHeight:this.options.arrows.middle.imageHeight,fromArrow:A,fromArrowScale:this.options.arrows.from.scaleFactor,fromArrowType:this.options.arrows.from.type,fromArrowSrc:this.options.arrows.from.src,fromArrowImageWidth:this.options.arrows.from.imageWidth,fromArrowImageHeight:this.options.arrows.from.imageHeight,arrowStrikethrough:this.options.arrowStrikethrough,color:C?void 0:this.options.color.color,inheritsColor:C,opacity:this.options.color.opacity,hidden:this.options.hidden,length:this.options.length,shadow:this.options.shadow.enabled,shadowColor:this.options.shadow.color,shadowSize:this.options.shadow.size,shadowX:this.options.shadow.x,shadowY:this.options.shadow.y,dashes:this.options.dashes,width:this.options.width,background:this.options.background.enabled,backgroundColor:this.options.background.color,backgroundSize:this.options.background.size,backgroundDashes:this.options.background.dashes};if(this.selected||this.hover)if(!0===this.chooser){if(this.selected){const g=this.options.selectionWidth;"function"==typeof g?I.width=g(I.width):"number"==typeof g&&(I.width+=g),I.width=Math.max(I.width,.3/this.body.view.scale),I.color=this.options.color.highlight,I.shadow=this.options.shadow.enabled}else if(this.hover){const g=this.options.hoverWidth;"function"==typeof g?I.width=g(I.width):"number"==typeof g&&(I.width+=g),I.width=Math.max(I.width,.3/this.body.view.scale),I.color=this.options.color.hover,I.shadow=this.options.shadow.enabled}}else"function"==typeof this.chooser&&(this.chooser(I,this.options.id,this.selected,this.hover),void 0!==I.color&&(I.inheritsColor=!1),!1===I.shadow&&(I.shadowColor===this.options.shadow.color&&I.shadowSize===this.options.shadow.size&&I.shadowX===this.options.shadow.x&&I.shadowY===this.options.shadow.y||(I.shadow=!0)));else I.shadow=this.options.shadow.enabled,I.width=Math.max(I.width,.3/this.body.view.scale);return I}updateLabelModule(g){const A=[g,this.options,this.globalOptions,this.defaultOptions];this.labelModule.update(this.options,A),void 0!==this.labelModule.baseSize&&(this.baseFontSize=this.labelModule.baseSize)}updateEdgeType(){const g=this.options.smooth;let A=!1,t=!0;return void 0!==this.edgeType&&((this.edgeType instanceof Gm&&!0===g.enabled&&"dynamic"===g.type||this.edgeType instanceof Vm&&!0===g.enabled&&"cubicBezier"===g.type||this.edgeType instanceof jm&&!0===g.enabled&&"dynamic"!==g.type&&"cubicBezier"!==g.type||this.edgeType instanceof Ym&&!1===g.type.enabled)&&(t=!1),!0===t&&(A=this.cleanup())),!0===t?!0===g.enabled?"dynamic"===g.type?(A=!0,this.edgeType=new Gm(this.options,this.body,this.labelModule)):"cubicBezier"===g.type?this.edgeType=new Vm(this.options,this.body,this.labelModule):this.edgeType=new jm(this.options,this.body,this.labelModule):this.edgeType=new Ym(this.options,this.body,this.labelModule):this.edgeType.setOptions(this.options),A}connect(){this.disconnect(),this.from=this.body.nodes[this.fromId]||void 0,this.to=this.body.nodes[this.toId]||void 0,this.connected=void 0!==this.from&&void 0!==this.to,!0===this.connected?(this.from.attachEdge(this),this.to.attachEdge(this)):(this.from&&this.from.detachEdge(this),this.to&&this.to.detachEdge(this)),this.edgeType.connect()}disconnect(){this.from&&(this.from.detachEdge(this),this.from=void 0),this.to&&(this.to.detachEdge(this),this.to=void 0),this.connected=!1}getTitle(){return this.title}isSelected(){return this.selected}getValue(){return this.options.value}setValueRange(g,A,t){if(void 0!==this.options.value){const C=this.options.scaling.customScalingFunction(g,A,t,this.options.value),I=this.options.scaling.max-this.options.scaling.min;if(!0===this.options.scaling.label.enabled){const g=this.options.scaling.label.max-this.options.scaling.label.min;this.options.font.size=this.options.scaling.label.min+C*g}this.options.width=this.options.scaling.min+C*I}else this.options.width=this.baseWidth,this.options.font.size=this.baseFontSize;this._setInteractionWidths(),this.updateLabelModule()}_setInteractionWidths(){"function"==typeof this.options.hoverWidth?this.edgeType.hoverWidth=this.options.hoverWidth(this.options.width):this.edgeType.hoverWidth=this.options.hoverWidth+this.options.width,"function"==typeof this.options.selectionWidth?this.edgeType.selectionWidth=this.options.selectionWidth(this.options.width):this.edgeType.selectionWidth=this.options.selectionWidth+this.options.width}draw(g){const A=this.getFormattingValues();if(A.hidden)return;const t=this.edgeType.getViaNode();this.edgeType.drawLine(g,A,this.selected,this.hover,t),this.drawLabel(g,t)}drawArrows(g){const A=this.getFormattingValues();if(A.hidden)return;const t=this.edgeType.getViaNode(),C={};this.edgeType.fromPoint=this.edgeType.from,this.edgeType.toPoint=this.edgeType.to,A.fromArrow&&(C.from=this.edgeType.getArrowData(g,"from",t,this.selected,this.hover,A),!1===A.arrowStrikethrough&&(this.edgeType.fromPoint=C.from.core),A.fromArrowSrc&&(C.from.image=this.imagelist.load(A.fromArrowSrc)),A.fromArrowImageWidth&&(C.from.imageWidth=A.fromArrowImageWidth),A.fromArrowImageHeight&&(C.from.imageHeight=A.fromArrowImageHeight)),A.toArrow&&(C.to=this.edgeType.getArrowData(g,"to",t,this.selected,this.hover,A),!1===A.arrowStrikethrough&&(this.edgeType.toPoint=C.to.core),A.toArrowSrc&&(C.to.image=this.imagelist.load(A.toArrowSrc)),A.toArrowImageWidth&&(C.to.imageWidth=A.toArrowImageWidth),A.toArrowImageHeight&&(C.to.imageHeight=A.toArrowImageHeight)),A.middleArrow&&(C.middle=this.edgeType.getArrowData(g,"middle",t,this.selected,this.hover,A),A.middleArrowSrc&&(C.middle.image=this.imagelist.load(A.middleArrowSrc)),A.middleArrowImageWidth&&(C.middle.imageWidth=A.middleArrowImageWidth),A.middleArrowImageHeight&&(C.middle.imageHeight=A.middleArrowImageHeight)),A.fromArrow&&this.edgeType.drawArrowHead(g,A,this.selected,this.hover,C.from),A.middleArrow&&this.edgeType.drawArrowHead(g,A,this.selected,this.hover,C.middle),A.toArrow&&this.edgeType.drawArrowHead(g,A,this.selected,this.hover,C.to)}drawLabel(g,A){if(void 0!==this.options.label){const t=this.from,C=this.to;let I;if(this.labelModule.differentState(this.selected,this.hover)&&this.labelModule.getTextSize(g,this.selected,this.hover),t.id!=C.id){this.labelModule.pointToSelf=!1,I=this.edgeType.getPoint(.5,A),g.save();const t=this._getRotation(g);0!=t.angle&&(g.translate(t.x,t.y),g.rotate(t.angle)),this.labelModule.draw(g,I.x,I.y,this.selected,this.hover),g.restore()}else{this.labelModule.pointToSelf=!0;const A=fm(g,this.options.selfReference.angle,this.options.selfReference.size,t);I=this._pointOnCircle(A.x,A.y,this.options.selfReference.size,this.options.selfReference.angle),this.labelModule.draw(g,I.x,I.y,this.selected,this.hover)}}}getItemsOnPoint(g){const A=[];if(this.labelModule.visible()){const t=this._getRotation();um(this.labelModule.getSize(),g,t)&&A.push({edgeId:this.id,labelId:0})}const t={left:g.x,top:g.y};return this.isOverlappingWith(t)&&A.push({edgeId:this.id}),A}isOverlappingWith(g){if(this.connected){const A=10,t=this.from.x,C=this.from.y,I=this.to.x,e=this.to.y,i=g.left,o=g.top;return this.edgeType.getDistanceToEdge(t,C,I,e,i,o)0&&e<0)&&(i+=Math.PI),C.angle=i,C}_pointOnCircle(g,A,t,C){return{x:g+t*Math.cos(C),y:A-t*Math.sin(C)}}select(){this.selected=!0}unselect(){this.selected=!1}cleanup(){return this.edgeType.cleanup()}remove(){this.cleanup(),this.disconnect(),delete this.body.edges[this.id]}endPointsValid(){return void 0!==this.body.nodes[this.fromId]&&void 0!==this.body.nodes[this.toId]}}var sy,ry,ay,dy,hy,ly,cy,uy={};function py(){if(sy)return uy;sy=1;var g=st(),A=y(),t=jI().f;return g({target:"Object",stat:!0,forced:A(function(){return!Object.getOwnPropertyNames(1)})},{getOwnPropertyNames:t}),uy}function fy(){if(ay)return ry;ay=1,py();var g=Tg().Object;return ry=function(A){return g.getOwnPropertyNames(A)},ry}function vy(){return hy?dy:(hy=1,dy=fy())}var by=C(cy?ly:(cy=1,ly=vy()));class my{constructor(g,A,t){this.body=A,this.labelModule=t,this.setOptions(g),this.top=void 0,this.left=void 0,this.height=void 0,this.width=void 0,this.radius=void 0,this.margin=void 0,this.refreshNeeded=!0,this.boundingBox={top:0,left:0,right:0,bottom:0}}setOptions(g){this.options=g}_setMargins(g){this.margin={},this.options.margin&&("object"==typeof this.options.margin?(this.margin.top=this.options.margin.top,this.margin.right=this.options.margin.right,this.margin.bottom=this.options.margin.bottom,this.margin.left=this.options.margin.left):(this.margin.top=this.options.margin,this.margin.right=this.options.margin,this.margin.bottom=this.options.margin,this.margin.left=this.options.margin)),g.adjustSizes(this.margin)}_distanceToBorder(g,A){const t=this.options.borderWidth;return g&&this.resize(g),Math.min(Math.abs(this.width/2/Math.cos(A)),Math.abs(this.height/2/Math.sin(A)))+t}enableShadow(g,A){A.shadow&&(g.shadowColor=A.shadowColor,g.shadowBlur=A.shadowSize,g.shadowOffsetX=A.shadowX,g.shadowOffsetY=A.shadowY)}disableShadow(g,A){A.shadow&&(g.shadowColor="rgba(0,0,0,0)",g.shadowBlur=0,g.shadowOffsetX=0,g.shadowOffsetY=0)}enableBorderDashes(g,A){if(!1!==A.borderDashes)if(void 0!==g.setLineDash){let t=A.borderDashes;!0===t&&(t=[5,15]),g.setLineDash(t)}else console.warn("setLineDash is not supported in this browser. The dashed borders cannot be used."),this.options.shapeProperties.borderDashes=!1,A.borderDashes=!1}disableBorderDashes(g,A){!1!==A.borderDashes&&(void 0!==g.setLineDash?g.setLineDash([0]):(console.warn("setLineDash is not supported in this browser. The dashed borders cannot be used."),this.options.shapeProperties.borderDashes=!1,A.borderDashes=!1))}needsRefresh(g,A){return!0===this.refreshNeeded?(this.refreshNeeded=!1,!0):void 0===this.width||this.labelModule.differentState(g,A)}initContextForDraw(g,A){const t=A.borderWidth/this.body.view.scale;g.lineWidth=Math.min(this.width,t),g.strokeStyle=A.borderColor,g.fillStyle=A.color}performStroke(g,A){const t=A.borderWidth/this.body.view.scale;g.save(),t>0&&(this.enableBorderDashes(g,A),g.stroke(),this.disableBorderDashes(g,A)),g.restore()}performFill(g,A){g.save(),g.fillStyle=A.color,this.enableShadow(g,A),ic(g).call(g),this.disableShadow(g,A),g.restore(),this.performStroke(g,A)}_addBoundingBoxMargin(g){this.boundingBox.left-=g,this.boundingBox.top-=g,this.boundingBox.bottom+=g,this.boundingBox.right+=g}_updateBoundingBox(g,A,t,C,I){void 0!==t&&this.resize(t,C,I),this.left=g-this.width/2,this.top=A-this.height/2,this.boundingBox.left=this.left,this.boundingBox.top=this.top,this.boundingBox.bottom=this.top+this.height,this.boundingBox.right=this.left+this.width}updateBoundingBox(g,A,t,C,I){this._updateBoundingBox(g,A,t,C,I)}getDimensionsFromLabel(g,A,t){this.textSize=this.labelModule.getTextSize(g,A,t);let C=this.textSize.width,I=this.textSize.height;return 0===C&&(C=14,I=14),{width:C,height:I}}}class yy extends my{constructor(g,A,t){super(g,A,t),this._setMargins(t)}resize(g){let A=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.selected,t=arguments.length>2&&void 0!==arguments[2]?arguments[2]:this.hover;if(this.needsRefresh(A,t)){const C=this.getDimensionsFromLabel(g,A,t);this.width=C.width+this.margin.right+this.margin.left,this.height=C.height+this.margin.top+this.margin.bottom,this.radius=this.width/2}}draw(g,A,t,C,I,e){this.resize(g,C,I),this.left=A-this.width/2,this.top=t-this.height/2,this.initContextForDraw(g,e),iC(g,this.left,this.top,this.width,this.height,e.borderRadius),this.performFill(g,e),this.updateBoundingBox(A,t,g,C,I),this.labelModule.draw(g,this.left+this.textSize.width/2+this.margin.left,this.top+this.textSize.height/2+this.margin.top,C,I)}updateBoundingBox(g,A,t,C,I){this._updateBoundingBox(g,A,t,C,I);const e=this.options.shapeProperties.borderRadius;this._addBoundingBoxMargin(e)}distanceToBorder(g,A){g&&this.resize(g);const t=this.options.borderWidth;return Math.min(Math.abs(this.width/2/Math.cos(A)),Math.abs(this.height/2/Math.sin(A)))+t}}class wy extends my{constructor(g,A,t){super(g,A,t),this.labelOffset=0,this.selected=!1}setOptions(g,A,t){this.options=g,void 0===A&&void 0===t||this.setImages(A,t)}setImages(g,A){A&&this.selected?(this.imageObj=A,this.imageObjAlt=g):(this.imageObj=g,this.imageObjAlt=A)}switchImages(g){const A=g&&!this.selected||!g&&this.selected;if(this.selected=g,void 0!==this.imageObjAlt&&A){const g=this.imageObj;this.imageObj=this.imageObjAlt,this.imageObjAlt=g}}_getImagePadding(){const g={top:0,right:0,bottom:0,left:0};if(this.options.imagePadding){const A=this.options.imagePadding;"object"==typeof A?(g.top=A.top,g.right=A.right,g.bottom=A.bottom,g.left=A.left):(g.top=A,g.right=A,g.bottom=A,g.left=A)}return g}_resizeImage(){let g,A;if(!1===this.options.shapeProperties.useImageSize){let t=1,C=1;this.imageObj.width&&this.imageObj.height&&(this.imageObj.width>this.imageObj.height?t=this.imageObj.width/this.imageObj.height:C=this.imageObj.height/this.imageObj.width),g=2*this.options.size*t,A=2*this.options.size*C}else{const t=this._getImagePadding();g=this.imageObj.width+t.left+t.right,A=this.imageObj.height+t.top+t.bottom}this.width=g,this.height=A,this.radius=.5*this.width}_drawRawCircle(g,A,t,C){this.initContextForDraw(g,C),eC(g,A,t,C.size),this.performFill(g,C)}_drawImageAtPosition(g,A){if(0!=this.imageObj.width){g.globalAlpha=void 0!==A.opacity?A.opacity:1,this.enableShadow(g,A);let t=1;!0===this.options.shapeProperties.interpolation&&(t=this.imageObj.width/this.width/this.body.view.scale);const C=this._getImagePadding(),I=this.left+C.left,e=this.top+C.top,i=this.width-C.left-C.right,o=this.height-C.top-C.bottom;this.imageObj.drawImageAtPosition(g,t,I,e,i,o),this.disableShadow(g,A)}}_drawImageLabel(g,A,t,C,I){let e=0;if(void 0!==this.height){e=.5*this.height;const A=this.labelModule.getTextSize(g,C,I);A.lineCount>=1&&(e+=A.height/2)}const i=t+e;this.options.label&&(this.labelOffset=e),this.labelModule.draw(g,A,i,C,I,"hanging")}}class xy extends wy{constructor(g,A,t){super(g,A,t),this._setMargins(t)}resize(g){let A=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.selected,t=arguments.length>2&&void 0!==arguments[2]?arguments[2]:this.hover;if(this.needsRefresh(A,t)){const C=this.getDimensionsFromLabel(g,A,t),I=Math.max(C.width+this.margin.right+this.margin.left,C.height+this.margin.top+this.margin.bottom);this.options.size=I/2,this.width=I,this.height=I,this.radius=this.width/2}}draw(g,A,t,C,I,e){this.resize(g,C,I),this.left=A-this.width/2,this.top=t-this.height/2,this._drawRawCircle(g,A,t,e),this.updateBoundingBox(A,t),this.labelModule.draw(g,this.left+this.textSize.width/2+this.margin.left,t,C,I)}updateBoundingBox(g,A){this.boundingBox.top=A-this.options.size,this.boundingBox.left=g-this.options.size,this.boundingBox.right=g+this.options.size,this.boundingBox.bottom=A+this.options.size}distanceToBorder(g){return g&&this.resize(g),.5*this.width}}class Ey extends wy{constructor(g,A,t,C,I){super(g,A,t),this.setImages(C,I)}resize(g){let A=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.selected,t=arguments.length>2&&void 0!==arguments[2]?arguments[2]:this.hover;if(void 0===this.imageObj.src||void 0===this.imageObj.width||void 0===this.imageObj.height){const g=2*this.options.size;return this.width=g,this.height=g,void(this.radius=.5*this.width)}this.needsRefresh(A,t)&&this._resizeImage()}draw(g,A,t,C,I,e){this.switchImages(C),this.resize();let i=A,o=t;"top-left"===this.options.shapeProperties.coordinateOrigin?(this.left=A,this.top=t,i+=this.width/2,o+=this.height/2):(this.left=A-this.width/2,this.top=t-this.height/2),this._drawRawCircle(g,i,o,e),g.save(),g.clip(),this._drawImageAtPosition(g,e),g.restore(),this._drawImageLabel(g,i,o,C,I),this.updateBoundingBox(A,t)}updateBoundingBox(g,A){"top-left"===this.options.shapeProperties.coordinateOrigin?(this.boundingBox.top=A,this.boundingBox.left=g,this.boundingBox.right=g+2*this.options.size,this.boundingBox.bottom=A+2*this.options.size):(this.boundingBox.top=A-this.options.size,this.boundingBox.left=g-this.options.size,this.boundingBox.right=g+this.options.size,this.boundingBox.bottom=A+this.options.size),this.boundingBox.left=Math.min(this.boundingBox.left,this.labelModule.size.left),this.boundingBox.right=Math.max(this.boundingBox.right,this.labelModule.size.left+this.labelModule.size.width),this.boundingBox.bottom=Math.max(this.boundingBox.bottom,this.boundingBox.bottom+this.labelOffset)}distanceToBorder(g){return g&&this.resize(g),.5*this.width}}class Oy extends my{resize(g){let A=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.selected,t=arguments.length>2&&void 0!==arguments[2]?arguments[2]:this.hover,C=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{size:this.options.size};if(this.needsRefresh(A,t)){var I,e;this.labelModule.getTextSize(g,A,t);const i=2*C.size;this.width=null!==(I=this.customSizeWidth)&&void 0!==I?I:i,this.height=null!==(e=this.customSizeHeight)&&void 0!==e?e:i,this.radius=.5*this.width}}_drawShape(g,A,t,C,I,e,i,o){var n;return this.resize(g,e,i,o),this.left=C-this.width/2,this.top=I-this.height/2,this.initContextForDraw(g,o),(n=A,Object.prototype.hasOwnProperty.call(rC,n)?rC[n]:function(g){for(var A=arguments.length,t=new Array(A>1?A-1:0),C=1;C{if(void 0!==this.options.label){this.labelModule.calculateLabelSize(g,e,i,C,I,"hanging");const A=I+.5*this.height+.5*this.labelModule.size.height;this.labelModule.draw(g,C,A,e,i,"hanging")}this.updateBoundingBox(C,I)}}}updateBoundingBox(g,A){this.boundingBox.top=A-this.options.size,this.boundingBox.left=g-this.options.size,this.boundingBox.right=g+this.options.size,this.boundingBox.bottom=A+this.options.size,void 0!==this.options.label&&this.labelModule.size.width>0&&(this.boundingBox.left=Math.min(this.boundingBox.left,this.labelModule.size.left),this.boundingBox.right=Math.max(this.boundingBox.right,this.labelModule.size.left+this.labelModule.size.width),this.boundingBox.bottom=Math.max(this.boundingBox.bottom,this.boundingBox.bottom+this.labelModule.size.height))}}function Ty(g,A){var t=Fr(g);if(Ob){var C=Ob(g);A&&(C=eh(C).call(C,function(A){return Fb(g,A).enumerable})),t.push.apply(t,C)}return t}function Dy(g){for(var A=1;A{g.save(),A(),g.restore()}}return i.nodeDimensions&&(this.customSizeWidth=i.nodeDimensions.width,this.customSizeHeight=i.nodeDimensions.height),i}distanceToBorder(g,A){return this._distanceToBorder(g,A)}}class ky extends my{constructor(g,A,t){super(g,A,t),this._setMargins(t)}resize(g,A,t){if(this.needsRefresh(A,t)){const C=this.getDimensionsFromLabel(g,A,t).width+this.margin.right+this.margin.left;this.width=C,this.height=C,this.radius=this.width/2}}draw(g,A,t,C,I,e){this.resize(g,C,I),this.left=A-this.width/2,this.top=t-this.height/2,this.initContextForDraw(g,e),nC(g,A-this.width/2,t-this.height/2,this.width,this.height),this.performFill(g,e),this.updateBoundingBox(A,t,g,C,I),this.labelModule.draw(g,this.left+this.textSize.width/2+this.margin.left,this.top+this.textSize.height/2+this.margin.top,C,I)}distanceToBorder(g,A){return this._distanceToBorder(g,A)}}class Ry extends Oy{draw(g,A,t,C,I,e){return this._drawShape(g,"diamond",4,A,t,C,I,e)}distanceToBorder(g,A){return this._distanceToBorder(g,A)}}class Py extends Oy{draw(g,A,t,C,I,e){return this._drawShape(g,"circle",2,A,t,C,I,e)}distanceToBorder(g){return g&&this.resize(g),this.options.size}}class My extends my{resize(g){let A=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.selected,t=arguments.length>2&&void 0!==arguments[2]?arguments[2]:this.hover;if(this.needsRefresh(A,t)){const C=this.getDimensionsFromLabel(g,A,t);this.height=2*C.height,this.width=C.width+C.height,this.radius=.5*this.width}}draw(g,A,t,C,I,e){this.resize(g,C,I),this.left=A-.5*this.width,this.top=t-.5*this.height,this.initContextForDraw(g,e),oC(g,this.left,this.top,this.width,this.height),this.performFill(g,e),this.updateBoundingBox(A,t,g,C,I),this.labelModule.draw(g,A,t,C,I)}distanceToBorder(g,A){g&&this.resize(g);const t=.5*this.width,C=.5*this.height,I=Math.sin(A)*t,e=Math.cos(A)*C;return t*C/Math.sqrt(I*I+e*e)}}class zy extends Oy{draw(g,A,t,C,I,e){return this._drawShape(g,"hexagon",4,A,t,C,I,e)}distanceToBorder(g,A){return this._distanceToBorder(g,A)}}class By extends my{constructor(g,A,t){super(g,A,t),this._setMargins(t)}resize(g,A,t){this.needsRefresh(A,t)&&(this.iconSize={width:Number(this.options.icon.size),height:Number(this.options.icon.size)},this.width=this.iconSize.width+this.margin.right+this.margin.left,this.height=this.iconSize.height+this.margin.top+this.margin.bottom,this.radius=.5*this.width)}draw(g,A,t,C,I,e){return this.resize(g,C,I),this.options.icon.size=this.options.icon.size||50,this.left=A-this.width/2,this.top=t-this.height/2,this._icon(g,A,t,C,I,e),{drawExternalLabel:()=>{if(void 0!==this.options.label){const A=5;this.labelModule.draw(g,this.left+this.iconSize.width/2+this.margin.left,t+this.height/2+A,C)}this.updateBoundingBox(A,t)}}}updateBoundingBox(g,A){if(this.boundingBox.top=A-.5*this.options.icon.size,this.boundingBox.left=g-.5*this.options.icon.size,this.boundingBox.right=g+.5*this.options.icon.size,this.boundingBox.bottom=A+.5*this.options.icon.size,void 0!==this.options.label&&this.labelModule.size.width>0){const g=5;this.boundingBox.left=Math.min(this.boundingBox.left,this.labelModule.size.left),this.boundingBox.right=Math.max(this.boundingBox.right,this.labelModule.size.left+this.labelModule.size.width),this.boundingBox.bottom=Math.max(this.boundingBox.bottom,this.boundingBox.bottom+this.labelModule.size.height+g)}}_icon(g,A,t,C,I,e){const i=Number(this.options.icon.size);void 0!==this.options.icon.code?(g.font=[null!=this.options.icon.weight?this.options.icon.weight:C?"bold":"",(null!=this.options.icon.weight&&C?5:0)+i+"px",this.options.icon.face].join(" "),g.fillStyle=this.options.icon.color||"black",g.textAlign="center",g.textBaseline="middle",this.enableShadow(g,e),g.fillText(this.options.icon.code,A,t),this.disableShadow(g,e)):console.error("When using the icon shape, you need to define the code in the icon options object. This can be done per node or globally.")}distanceToBorder(g,A){return this._distanceToBorder(g,A)}}let Zy=class extends wy{constructor(g,A,t,C,I){super(g,A,t),this.setImages(C,I)}resize(g){let A=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.selected,t=arguments.length>2&&void 0!==arguments[2]?arguments[2]:this.hover;if(void 0===this.imageObj.src||void 0===this.imageObj.width||void 0===this.imageObj.height){const g=2*this.options.size;return this.width=g,void(this.height=g)}this.needsRefresh(A,t)&&this._resizeImage()}draw(g,A,t,C,I,e){g.save(),this.switchImages(C),this.resize();let i=A,o=t;if("top-left"===this.options.shapeProperties.coordinateOrigin?(this.left=A,this.top=t,i+=this.width/2,o+=this.height/2):(this.left=A-this.width/2,this.top=t-this.height/2),!0===this.options.shapeProperties.useBorderWithImage){const A=this.options.borderWidth,t=this.options.borderWidthSelected||2*this.options.borderWidth,i=(C?t:A)/this.body.view.scale;g.lineWidth=Math.min(this.width,i),g.beginPath();let o=C?this.options.color.highlight.border:I?this.options.color.hover.border:this.options.color.border,n=C?this.options.color.highlight.background:I?this.options.color.hover.background:this.options.color.background;void 0!==e.opacity&&(o=cp(o,e.opacity),n=cp(n,e.opacity)),g.strokeStyle=o,g.fillStyle=n,g.rect(this.left-.5*g.lineWidth,this.top-.5*g.lineWidth,this.width+g.lineWidth,this.height+g.lineWidth),ic(g).call(g),this.performStroke(g,e),g.closePath()}this._drawImageAtPosition(g,e),this._drawImageLabel(g,i,o,C,I),this.updateBoundingBox(A,t),g.restore()}updateBoundingBox(g,A){this.resize(),"top-left"===this.options.shapeProperties.coordinateOrigin?(this.left=g,this.top=A):(this.left=g-this.width/2,this.top=A-this.height/2),this.boundingBox.left=this.left,this.boundingBox.top=this.top,this.boundingBox.bottom=this.top+this.height,this.boundingBox.right=this.left+this.width,void 0!==this.options.label&&this.labelModule.size.width>0&&(this.boundingBox.left=Math.min(this.boundingBox.left,this.labelModule.size.left),this.boundingBox.right=Math.max(this.boundingBox.right,this.labelModule.size.left+this.labelModule.size.width),this.boundingBox.bottom=Math.max(this.boundingBox.bottom,this.boundingBox.bottom+this.labelOffset))}distanceToBorder(g,A){return this._distanceToBorder(g,A)}};class Sy extends Oy{draw(g,A,t,C,I,e){return this._drawShape(g,"square",2,A,t,C,I,e)}distanceToBorder(g,A){return this._distanceToBorder(g,A)}}class Fy extends Oy{draw(g,A,t,C,I,e){return this._drawShape(g,"star",4,A,t,C,I,e)}distanceToBorder(g,A){return this._distanceToBorder(g,A)}}class Gy extends my{constructor(g,A,t){super(g,A,t),this._setMargins(t)}resize(g,A,t){this.needsRefresh(A,t)&&(this.textSize=this.labelModule.getTextSize(g,A,t),this.width=this.textSize.width+this.margin.right+this.margin.left,this.height=this.textSize.height+this.margin.top+this.margin.bottom,this.radius=.5*this.width)}draw(g,A,t,C,I,e){this.resize(g,C,I),this.left=A-this.width/2,this.top=t-this.height/2,this.enableShadow(g,e),this.labelModule.draw(g,this.left+this.textSize.width/2+this.margin.left,this.top+this.textSize.height/2+this.margin.top,C,I),this.disableShadow(g,e),this.updateBoundingBox(A,t,g,C,I)}distanceToBorder(g,A){return this._distanceToBorder(g,A)}}class jy extends Oy{draw(g,A,t,C,I,e){return this._drawShape(g,"triangle",3,A,t,C,I,e)}distanceToBorder(g,A){return this._distanceToBorder(g,A)}}class Ly extends Oy{draw(g,A,t,C,I,e){return this._drawShape(g,"triangleDown",3,A,t,C,I,e)}distanceToBorder(g,A){return this._distanceToBorder(g,A)}}function Vy(g,A){var t=Fr(g);if(Ob){var C=Ob(g);A&&(C=eh(C).call(C,function(A){return Fb(g,A).enumerable})),t.push.apply(t,C)}return t}function Yy(g){for(var A=1;Anull!=A[g]);i.push("font"),np(i,g,e),g.color=pp(g.color)}static parseOptions(g,A){let t=arguments.length>2&&void 0!==arguments[2]&&arguments[2],C=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{},I=arguments.length>4?arguments[4]:void 0;if(np(["color","fixed","shadow"],g,A,t),Wy.checkMass(A),void 0!==g.opacity&&(Wy.checkOpacity(g.opacity)||(console.error("Invalid option for node opacity. Value must be between 0 and 1, found: "+g.opacity),g.opacity=void 0)),void 0!==A.opacity&&(Wy.checkOpacity(A.opacity)||(console.error("Invalid option for node opacity. Value must be between 0 and 1, found: "+A.opacity),A.opacity=void 0)),A.shapeProperties&&!Wy.checkCoordinateOrigin(A.shapeProperties.coordinateOrigin)&&console.error("Invalid option for node coordinateOrigin, found: "+A.shapeProperties.coordinateOrigin),xp(g,A,"shadow",C),void 0!==A.color&&null!==A.color){const t=pp(A.color);ip(g.color,t)}else!0===t&&null===A.color&&(g.color=wp(C.color));void 0!==A.fixed&&null!==A.fixed&&("boolean"==typeof A.fixed?(g.fixed.x=A.fixed,g.fixed.y=A.fixed):(void 0!==A.fixed.x&&"boolean"==typeof A.fixed.x&&(g.fixed.x=A.fixed.x),void 0!==A.fixed.y&&"boolean"==typeof A.fixed.y&&(g.fixed.y=A.fixed.y))),!0===t&&null===A.font&&(g.font=wp(C.font)),Wy.updateGroupOptions(g,A,I),void 0!==A.scaling&&xp(g.scaling,A.scaling,"label",C.scaling)}getFormattingValues(){const g={color:this.options.color.background,opacity:this.options.opacity,borderWidth:this.options.borderWidth,borderColor:this.options.color.border,size:this.options.size,borderDashes:this.options.shapeProperties.borderDashes,borderRadius:this.options.shapeProperties.borderRadius,shadow:this.options.shadow.enabled,shadowColor:this.options.shadow.color,shadowSize:this.options.shadow.size,shadowX:this.options.shadow.x,shadowY:this.options.shadow.y};if(this.selected||this.hover?!0===this.chooser?this.selected?(null!=this.options.borderWidthSelected?g.borderWidth=this.options.borderWidthSelected:g.borderWidth*=2,g.color=this.options.color.highlight.background,g.borderColor=this.options.color.highlight.border,g.shadow=this.options.shadow.enabled):this.hover&&(g.color=this.options.color.hover.background,g.borderColor=this.options.color.hover.border,g.shadow=this.options.shadow.enabled):"function"==typeof this.chooser&&(this.chooser(g,this.options.id,this.selected,this.hover),!1===g.shadow&&(g.shadowColor===this.options.shadow.color&&g.shadowSize===this.options.shadow.size&&g.shadowX===this.options.shadow.x&&g.shadowY===this.options.shadow.y||(g.shadow=!0))):g.shadow=this.options.shadow.enabled,void 0!==this.options.opacity){const A=this.options.opacity;g.borderColor=cp(g.borderColor,A),g.color=cp(g.color,A),g.shadowColor=cp(g.shadowColor,A)}return g}updateLabelModule(g){void 0!==this.options.label&&null!==this.options.label||(this.options.label=""),Wy.updateGroupOptions(this.options,Yy(Yy({},g),{},{color:g&&g.color||this._localColor||void 0}),this.grouplist);const A=this.grouplist.get(this.options.group,!1),t=[g,this.options,A,this.globalOptions,this.defaultOptions];this.labelModule.update(this.options,t),void 0!==this.labelModule.baseSize&&(this.baseFontSize=this.labelModule.baseSize)}updateShape(g){if(g===this.options.shape&&this.shape)this.shape.setOptions(this.options,this.imageObj,this.imageObjAlt);else switch(this.options.shape){case"box":this.shape=new yy(this.options,this.body,this.labelModule);break;case"circle":this.shape=new xy(this.options,this.body,this.labelModule);break;case"circularImage":this.shape=new Ey(this.options,this.body,this.labelModule,this.imageObj,this.imageObjAlt);break;case"custom":this.shape=new Ny(this.options,this.body,this.labelModule,this.options.ctxRenderer);break;case"database":this.shape=new ky(this.options,this.body,this.labelModule);break;case"diamond":this.shape=new Ry(this.options,this.body,this.labelModule);break;case"dot":this.shape=new Py(this.options,this.body,this.labelModule);break;case"ellipse":default:this.shape=new My(this.options,this.body,this.labelModule);break;case"icon":this.shape=new By(this.options,this.body,this.labelModule);break;case"image":this.shape=new Zy(this.options,this.body,this.labelModule,this.imageObj,this.imageObjAlt);break;case"square":this.shape=new Sy(this.options,this.body,this.labelModule);break;case"hexagon":this.shape=new zy(this.options,this.body,this.labelModule);break;case"star":this.shape=new Fy(this.options,this.body,this.labelModule);break;case"text":this.shape=new Gy(this.options,this.body,this.labelModule);break;case"triangle":this.shape=new jy(this.options,this.body,this.labelModule);break;case"triangleDown":this.shape=new Ly(this.options,this.body,this.labelModule)}this.needsRefresh()}select(){this.selected=!0,this.needsRefresh()}unselect(){this.selected=!1,this.needsRefresh()}needsRefresh(){this.shape.refreshNeeded=!0}getTitle(){return this.options.title}distanceToBorder(g,A){return this.shape.distanceToBorder(g,A)}isFixed(){return this.options.fixed.x&&this.options.fixed.y}isSelected(){return this.selected}getValue(){return this.options.value}getLabelSize(){return this.labelModule.size()}setValueRange(g,A,t){if(void 0!==this.options.value){const C=this.options.scaling.customScalingFunction(g,A,t,this.options.value),I=this.options.scaling.max-this.options.scaling.min;if(!0===this.options.scaling.label.enabled){const g=this.options.scaling.label.max-this.options.scaling.label.min;this.options.font.size=this.options.scaling.label.min+C*g}this.options.size=this.options.scaling.min+C*I}else this.options.size=this.baseSize,this.options.font.size=this.baseFontSize;this.updateLabelModule()}draw(g){const A=this.getFormattingValues();return this.shape.draw(g,this.x,this.y,this.selected,this.hover,A)||{}}updateBoundingBox(g){this.shape.updateBoundingBox(this.x,this.y,g)}resize(g){const A=this.getFormattingValues();this.shape.resize(g,this.selected,this.hover,A)}getItemsOnPoint(g){const A=[];return this.labelModule.visible()&&um(this.labelModule.getSize(),g)&&A.push({nodeId:this.id,labelId:0}),um(this.shape.boundingBox,g)&&A.push({nodeId:this.id}),A}isOverlappingWith(g){return this.shape.leftg.left&&this.shape.topg.top}isBoundingBoxOverlappingWith(g){return this.shape.boundingBox.leftg.left&&this.shape.boundingBox.topg.top}static checkMass(g,A){if(void 0!==g.mass&&g.mass<=0){let t="";void 0!==A&&(t=" in node id: "+A),console.error("%cNegative or zero mass disallowed"+t+", setting mass to 1.",Sp),g.mass=1}}}class Qy extends Wy{constructor(g,A,t,C,I,e){super(g,A,t,C,I,e),this.isCluster=!0,this.containedNodes={},this.containedEdges={}}_openChildCluster(g){const A=this.body.nodes[g];if(void 0===this.containedNodes[g])throw new Error("node with id: "+g+" not in current cluster");if(!A.isCluster)throw new Error("node with id: "+g+" is not a cluster");delete this.containedNodes[g],hp(A.edges,g=>{delete this.containedEdges[g.id]}),hp(A.containedNodes,(g,A)=>{this.containedNodes[A]=g}),A.containedNodes={},hp(A.containedEdges,(g,A)=>{this.containedEdges[A]=g}),A.containedEdges={},hp(A.edges,g=>{hp(this.edges,A=>{var t,C;const I=Jh(t=A.clusteringEdgeReplacingIds).call(t,g.id);-1!==I&&(hp(g.clusteringEdgeReplacingIds,g=>{A.clusteringEdgeReplacingIds.push(g),this.body.edges[g].edgeReplacedById=A.id}),Va(C=A.clusteringEdgeReplacingIds).call(C,I,1))})}),A.edges=[]}}class Uy{constructor(g){this.body=g,this.clusteredNodes={},this.clusteredEdges={},this.options={},this.defaultOptions={},_t(this.options,this.defaultOptions),this.body.emitter.on("_resetData",()=>{this.clusteredNodes={},this.clusteredEdges={}})}clusterByHubsize(g,A){void 0===g?g=this._getHubSize():"object"==typeof g&&(A=this._checkOptions(g),g=this._getHubSize());const t=[];for(let A=0;A=g&&t.push(C.id)}for(let g=0;g0&&void 0!==arguments[0]?arguments[0]:{},A=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];if(void 0===g.joinCondition)throw new Error("Cannot call clusterByNodeData without a joinCondition function in the options.");g=this._checkOptions(g);const t={},C={};hp(this.body.nodes,(A,I)=>{A.options&&!0===g.joinCondition(A.options)&&(t[I]=A,hp(A.edges,g=>{void 0===this.clusteredEdges[g.id]&&(C[g.id]=g)}))}),this._cluster(t,C,g,A)}clusterByEdgeCount(g,A){let t=!(arguments.length>2&&void 0!==arguments[2])||arguments[2];A=this._checkOptions(A);const C=[],I={};let e,i,o;for(let t=0;t0&&Fr(s).length>0&&!0===t){const g=function(){for(let g=0;g1&&void 0!==arguments[1])||arguments[1];this.clusterByEdgeCount(1,g,A)}clusterBridges(g){let A=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];this.clusterByEdgeCount(2,g,A)}clusterByConnection(g,A){var t;let C=!(arguments.length>2&&void 0!==arguments[2])||arguments[2];if(void 0===g)throw new Error("No nodeId supplied to clusterByConnection!");if(void 0===this.body.nodes[g])throw new Error("The nodeId given to clusterByConnection does not exist!");const I=this.body.nodes[g];void 0===(A=this._checkOptions(A,I)).clusterNodeProperties.x&&(A.clusterNodeProperties.x=I.x),void 0===A.clusterNodeProperties.y&&(A.clusterNodeProperties.y=I.y),void 0===A.clusterNodeProperties.fixed&&(A.clusterNodeProperties.fixed={},A.clusterNodeProperties.fixed.x=I.options.fixed.x,A.clusterNodeProperties.fixed.y=I.options.fixed.y);const e={},i={},o=I.id,n=Ff.cloneOptions(I);e[o]=I;for(let g=0;g-1&&(i[t.id]=t)}}this._cluster(e,i,A,C)}_createClusterEdges(g,A,t,C){let I,e,i,o,n,s;const r=Fr(g),a=[];for(let C=0;C0&&void 0!==arguments[0]?arguments[0]:{};return void 0===g.clusterEdgeProperties&&(g.clusterEdgeProperties={}),void 0===g.clusterNodeProperties&&(g.clusterNodeProperties={}),g}_cluster(g,A,t){let C=!(arguments.length>3&&void 0!==arguments[3])||arguments[3];const I=[];for(const A in g)Object.prototype.hasOwnProperty.call(g,A)&&void 0!==this.clusteredNodes[A]&&I.push(A);for(let A=0;AI?t.x:I,e=t.yi?t.y:i;return{x:.5*(C+I),y:.5*(e+i)}}openCluster(g,A){let t=!(arguments.length>2&&void 0!==arguments[2])||arguments[2];if(void 0===g)throw new Error("No clusterNodeId supplied to openCluster.");const C=this.body.nodes[g];if(void 0===C)throw new Error("The clusterNodeId supplied to openCluster does not exist.");if(!0!==C.isCluster||void 0===C.containedNodes||void 0===C.containedEdges)throw new Error("The node:"+g+" is not a valid cluster.");const I=this.findNode(g),e=Jh(I).call(I,g)-1;if(e>=0){const A=I[e];return this.body.nodes[A]._openChildCluster(g),delete this.body.nodes[g],void(!0===t&&this.body.emitter.emit("_dataChanged"))}const i=C.containedNodes,o=C.containedEdges;if(void 0!==A&&void 0!==A.releaseFunction&&"function"==typeof A.releaseFunction){const g={},t={x:C.x,y:C.y};for(const A in i)if(Object.prototype.hasOwnProperty.call(i,A)){const t=this.body.nodes[A];g[A]={x:t.x,y:t.y}}const I=A.releaseFunction(t,g);for(const g in i)if(Object.prototype.hasOwnProperty.call(i,g)){const A=this.body.nodes[g];void 0!==I[g]&&(A.x=void 0===I[g].x?C.x:I[g].x,A.y=void 0===I[g].y?C.y:I[g].y)}}else hp(i,function(g){!1===g.options.fixed.x&&(g.x=C.x),!1===g.options.fixed.y&&(g.y=C.y)});for(const g in i)if(Object.prototype.hasOwnProperty.call(i,g)){const A=this.body.nodes[g];A.vx=C.vx,A.vy=C.vy,A.setOptions({physics:!0}),delete this.clusteredNodes[g]}const n=[];for(let g=0;g0&&I<100;){const g=A.pop();if(void 0===g)continue;const e=this.body.edges[g];if(void 0===e)continue;I++;const i=e.clusteringEdgeReplacingIds;if(void 0===i)C.push(g);else for(let g=0;gC&&(C=e.edges.length),g+=e.edges.length,A+=Math.pow(e.edges.length,2),t+=1}g/=t,A/=t;const I=A-Math.pow(g,2),e=Math.sqrt(I);let i=Math.floor(g+2*e);return i>C&&(i=C),i}_createClusteredEdge(g,A,t,C,I){const e=Ff.cloneOptions(t,"edge");sp(e,C),e.from=g,e.to=A,e.id="clusterEdge:"+Sf(),void 0!==I&&sp(e,I);const i=this.body.functions.createEdge(e);return i.clusteringEdgeReplacingIds=[t.id],i.connect(),this.body.edges[i.id]=i,i}_clusterEdges(g,A,t,C){if(A instanceof ny){const g=A,t={};t[g.id]=g,A=t}if(g instanceof Wy){const A=g,t={};t[A.id]=A,g=t}if(null==t)throw new Error("_clusterEdges: parameter clusterNode required");void 0===C&&(C=t.clusterEdgeProperties),this._createClusterEdges(g,A,t,C);for(const g in A)if(Object.prototype.hasOwnProperty.call(A,g)&&void 0!==this.body.edges[g]){const A=this.body.edges[g];this._backupEdgeOptions(A),A.setOptions({physics:!1})}for(const A in g)Object.prototype.hasOwnProperty.call(g,A)&&(this.clusteredNodes[A]={clusterId:t.id,node:this.body.nodes[A]},this.body.nodes[A].setOptions({physics:!1}))}_getClusterNodeForNode(g){if(void 0===g)return;const A=this.clusteredNodes[g];if(void 0===A)return;const t=A.clusterId;return void 0!==t?this.body.nodes[t]:void 0}_filter(g,A){const t=[];return hp(g,g=>{A(g)&&t.push(g)}),t}_updateState(){let g;const A=[],t={},C=g=>{hp(this.body.nodes,A=>{!0===A.isCluster&&g(A)})};for(g in this.clusteredNodes){if(!Object.prototype.hasOwnProperty.call(this.clusteredNodes,g))continue;void 0===this.body.nodes[g]&&A.push(g)}C(function(g){for(let t=0;t{const A=this.body.edges[g];void 0!==A&&A.endPointsValid()||(t[g]=g)}),C(function(g){hp(g.containedEdges,(g,A)=>{g.endPointsValid()||t[A]||(t[A]=A)})}),hp(this.body.edges,(g,A)=>{let C=!0;const I=g.clusteringEdgeReplacingIds;if(void 0!==I){let g=0;hp(I,A=>{const t=this.body.edges[A];void 0!==t&&t.endPointsValid()&&(g+=1)}),C=g>0}g.endPointsValid()&&C||(t[A]=A)}),C(g=>{hp(t,A=>{delete g.containedEdges[A],hp(g.edges,(C,I)=>{C.id!==A?C.clusteringEdgeReplacingIds=this._filter(C.clusteringEdgeReplacingIds,function(g){return!t[g]}):g.edges[I]=null}),g.edges=this._filter(g.edges,function(g){return null!==g})})}),hp(t,g=>{delete this.clusteredEdges[g]}),hp(t,g=>{delete this.body.edges[g]});hp(Fr(this.body.edges),g=>{const A=this.body.edges[g],t=this._isClusteredNode(A.fromId)||this._isClusteredNode(A.toId);if(t!==this._isClusteredEdge(A.id))if(t){const g=this._getClusterNodeForNode(A.fromId);void 0!==g&&this._clusterEdges(this.body.nodes[A.fromId],A,g);const t=this._getClusterNodeForNode(A.toId);void 0!==t&&this._clusterEdges(this.body.nodes[A.toId],A,t)}else delete this._clusterEdges[g],this._restoreEdge(A)});let I=!1,e=!0;for(;e;){const g=[];C(function(A){const t=Fr(A.containedNodes).length,C=!0===A.options.allowSingleNodeCluster;(C&&t<1||!C&&t<2)&&g.push(A.id)});for(let A=0;A0,I=I||e}I&&this._updateState()}_isClusteredNode(g){return void 0!==this.clusteredNodes[g]}_isClusteredEdge(g){return void 0!==this.clusteredEdges[g]}}var Ky,Hy,Xy,_y,Jy,qy,$y,gw,Aw,tw,Cw,Iw,ew,iw={};function ow(){if(Hy)return Ky;Hy=1;var g=Zg(),A=fA(),t=yg(),C=lt(),I=TypeError,e="Reduce of empty array with no initial value",i=function(i){return function(o,n,s,r){var a=A(o),d=t(a),h=C(a);if(g(n),0===h&&s<2)throw new I(e);var l=i?h-1:0,c=i?-1:1;if(s<2)for(;;){if(l in d){r=d[l],l+=c;break}if(l+=c,i?l<0:h<=l)throw new I(e)}for(;i?l>=0:h>l;l+=c)l in d&&(r=n(r,d[l],l,a));return r}};return Ky={left:i(!1),right:i(!0)}}function nw(){return _y?Xy:(_y=1,Xy="NODE"===kl())}function sw(){return $y?qy:($y=1,function(){if(Jy)return iw;Jy=1;var g=st(),A=ow().left,t=Ca(),C=Rg();g({target:"Array",proto:!0,forced:!nw()&&C>79&&C<83||!t("reduce")},{reduce:function(g){var t=arguments.length;return A(this,g,t,t>1?arguments[1]:void 0)}})}(),qy=gC()("Array","reduce"))}function rw(){if(Aw)return gw;Aw=1;var g=Ng(),A=sw(),t=Array.prototype;return gw=function(C){var I=C.reduce;return C===t||g(t,C)&&I===t.reduce?A:I},gw}function aw(){return Cw?tw:(Cw=1,tw=rw())}var dw,hw,lw,cw=C(ew?Iw:(ew=1,Iw=aw())),uw={};function pw(){if(hw)return dw;hw=1;var g=DC(),A=lt(),t=CI(),C=DA(),I=II(),e=function(i,o,n,s,r,a,d,h){for(var l,c,u=r,p=0,f=!!d&&C(d,h);p0&&g(l)?(c=A(l),u=e(i,o,l,c,u,a-1)-1):(t(u+1),I(i,u,l)),u++),p++;return u};return dw=e}var fw,vw,bw,mw,yw,ww,xw,Ew,Ow;function Tw(){return bw?vw:(bw=1,function(){if(lw)return uw;lw=1;var g=st(),A=pw(),t=Zg(),C=fA(),I=lt(),e=aI();g({target:"Array",proto:!0},{flatMap:function(g){var i,o=C(this),n=I(o);return t(g),i=e(o,0),A(i,o,o,n,0,1,g,arguments.length>1?arguments[1]:void 0),i}})}(),fw||(fw=1,Ui()("flatMap")),vw=gC()("Array","flatMap"))}function Dw(){if(yw)return mw;yw=1;var g=Ng(),A=Tw(),t=Array.prototype;return mw=function(C){var I=C.flatMap;return C===t||g(t,C)&&I===t.flatMap?A:I},mw}function Nw(){return xw?ww:(xw=1,ww=Dw())}var kw,Rw,Pw=C(Ow?Ew:(Ow=1,Ew=Nw()));var Mw,zw,Bw,Zw,Sw,Fw,Gw,jw,Lw,Vw,Yw,Ww,Qw,Uw,Kw,Hw,Xw,_w,Jw,qw,$w,gx=C(Rw?kw:(Rw=1,kw=Vv())),Ax={},tx={exports:{}};function Cx(){return zw?Mw:(zw=1,Mw=y()(function(){if("function"==typeof ArrayBuffer){var g=new ArrayBuffer(8);Object.isExtensible(g)&&Object.defineProperty(g,"a",{value:8})}}))}function Ix(){if(Zw)return Bw;Zw=1;var g=y(),A=Og(),t=O(),C=Cx(),I=Object.isExtensible,e=g(function(){});return Bw=e||C?function(g){return!!A(g)&&((!C||"ArrayBuffer"!==t(g))&&(!I||I(g)))}:I,Bw}function ex(){return Fw?Sw:(Fw=1,Sw=!y()(function(){return Object.isExtensible(Object.preventExtensions({}))}))}function ix(){if(Gw)return tx.exports;Gw=1;var g=st(),A=E(),t=ut(),C=Og(),I=vA(),e=ot().f,i=MI(),o=jI(),n=Ix(),s=bA(),r=ex(),a=!1,d=s("meta"),h=0,l=function(g){e(g,d,{value:{objectID:"O"+h++,weakData:{}}})},c=tx.exports={enable:function(){c.enable=function(){},a=!0;var t=i.f,C=A([].splice),I={};I[d]=1,t(I).length&&(i.f=function(g){for(var A=t(g),I=0,e=A.length;If;f++)if((b=k(h[f]))&&i(d,b))return b;return new a(!1)}u=o(h,p)}for(m=E?h.next:u.next;!(y=A(m,u)).done;){var R=y.value;try{b=k(R)}catch(g){if(!u)throw g;s(u,"throw",g)}if("object"==typeof b&&b&&i(d,b))return b}return new a(!1)},jw}function nx(){if(Yw)return Vw;Yw=1;var g=Ng(),A=TypeError;return Vw=function(t,C){if(g(C,t))return t;throw new A("Incorrect invocation")},Vw}function sx(){if(Qw)return Ww;Qw=1;var g=st(),A=m(),t=ix(),C=B(),I=y(),e=nt(),i=ox(),o=nx(),n=D(),s=Og(),r=wg(),a=re(),d=ot().f,h=he().forEach,l=z(),c=de(),u=c.set,p=c.getterFor;return Ww=function(c,f,v){var b,m=-1!==c.indexOf("Map"),y=-1!==c.indexOf("Weak"),w=m?"set":"add",x=A[c],E=x&&x.prototype,O={};if(l&&n(x)&&(y||E.forEach&&!I(function(){(new x).entries().next()}))){var T=(b=f(function(g,A){u(o(g,T),{type:c,collection:new x}),r(A)||i(A,g[w],{that:g,AS_ENTRIES:m})})).prototype,D=p(c);h(["add","clear","delete","forEach","get","has","set","keys","values","entries"],function(g){var A="add"===g||"set"===g;!(g in E)||y&&"clear"===g||e(T,g,function(t,I){var e=this,i=D(e).collection;if(!A&&y&&!s(t))return"get"===g&&void 0;var o=i[g]("forEach"===g?function(g,A){C(t,I,g,A,e)}:0===t?0:t,I);return A?e:o})}),y||d(T,"size",{configurable:!0,get:function(){return D(this).collection.size}})}else b=v.getConstructor(f,c,m,w),t.enable();return a(b,c,!1,!0),O[c]=b,g({global:!0,forced:!0},O),y||v.setStrong(b,c,m),b}}function rx(){if(Kw)return Uw;Kw=1;var g=LI();return Uw=function(A,t,C){for(var I in t)C&&C.unsafe&&A[I]?A[I]=t[I]:g(A,I,t[I],C);return A},Uw}function ax(){if(Xw)return Hw;Xw=1;var g=Dg(),A=VI(),t=mA(),C=z(),I=t("species");return Hw=function(t){var e=g(t);C&&e&&!e[I]&&A(e,I,{configurable:!0,get:function(){return this}})}}function dx(){if(Jw)return _w;Jw=1;var g=kI(),A=VI(),t=rx(),C=DA(),I=nx(),e=wg(),i=ox(),o=Co(),n=Io(),s=ax(),r=z(),a=ix().fastKey,d=de(),h=d.set,l=d.getterFor;return _w={getConstructor:function(o,n,s,d){var c=o(function(A,t){I(A,u),h(A,{type:n,index:g(null),first:null,last:null,size:0}),r||(A.size=0),e(t)||i(t,A[d],{that:A,AS_ENTRIES:s})}),u=c.prototype,p=l(n),f=function(g,A,t){var C,I,e=p(g),i=v(g,A);return i?i.value=t:(e.last=i={index:I=a(A,!0),key:A,value:t,previous:C=e.last,next:null,removed:!1},e.first||(e.first=i),C&&(C.next=i),r?e.size++:g.size++,"F"!==I&&(e.index[I]=i)),g},v=function(g,A){var t,C=p(g),I=a(A);if("F"!==I)return C.index[I];for(t=C.first;t;t=t.next)if(t.key===A)return t};return t(u,{clear:function(){for(var A=p(this),t=A.first;t;)t.removed=!0,t.previous&&(t.previous=t.previous.next=null),t=t.next;A.first=A.last=null,A.index=g(null),r?A.size=0:this.size=0},delete:function(g){var A=this,t=p(A),C=v(A,g);if(C){var I=C.next,e=C.previous;delete t.index[C.index],C.removed=!0,e&&(e.next=I),I&&(I.previous=e),t.first===C&&(t.first=I),t.last===C&&(t.last=e),r?t.size--:A.size--}return!!C},forEach:function(g){for(var A,t=p(this),I=C(g,arguments.length>1?arguments[1]:void 0);A=A?A.next:t.first;)for(I(A.value,A.key,this);A&&A.removed;)A=A.previous},has:function(g){return!!v(this,g)}}),t(u,s?{get:function(g){var A=v(this,g);return A&&A.value},set:function(g,A){return f(this,0===g?0:g,A)}}:{add:function(g){return f(this,g=0===g?0:g,g)}}),r&&A(u,"size",{configurable:!0,get:function(){return p(this).size}}),c},setStrong:function(g,A,t){var C=A+" Iterator",I=l(A),e=l(C);o(g,A,function(g,A){h(this,{type:C,target:g,state:I(g),kind:A,last:null})},function(){for(var g=e(this),A=g.kind,t=g.last;t&&t.removed;)t=t.previous;return g.target&&(g.last=t=t?t.next:g.state.first)?n("keys"===A?t.key:"values"===A?t.value:[t.key,t.value],!1):(g.target=null,n(void 0,!0))},t?"entries":"values",!t,!0),s(A)}},_w}function hx(){return $w||($w=1,qw||(qw=1,sx()("Map",function(g){return function(){return g(this,arguments.length?arguments[0]:void 0)}},dx()))),Ax}var lx,cx,ux,px,fx,vx={};function bx(){return cx||(cx=1,lx=function(g,A){return 1===A?function(A,t){return A[g](t)}:function(A,t,C){return A[g](t,C)}}),lx}function mx(){if(px)return ux;px=1;var g=Dg(),A=bx(),t=g("Map");return ux={Map:t,set:A("set",2),get:A("get",1),has:A("has",1),remove:A("delete",1),proto:t.prototype}}var yx,wx={};var xx,Ex,Ox,Tx,Dx,Nx,kx,Rx={};function Px(){return Ox?Ex:(Ox=1,eo(),hx(),function(){if(fx)return vx;fx=1;var g=st(),A=E(),t=Zg(),C=xg(),I=ox(),e=mx(),i=lA(),o=y(),n=e.Map,s=e.has,r=e.get,a=e.set,d=A([].push),h=i||o(function(){return 1!==n.groupBy("ab",function(g){return g}).get("a").length});g({target:"Map",stat:!0,forced:i||h},{groupBy:function(g,A){C(g),t(A);var e=new n,i=0;return I(g,function(g){var t=A(g,i++);s(e,t)?d(r(e,t),g):a(e,t,[g])}),e}})}(),function(){if(yx)return wx;yx=1;var g=st(),A=mx(),t=lA(),C=A.get,I=A.has,e=A.set;g({target:"Map",proto:!0,real:!0,forced:t},{getOrInsert:function(g,A){return I(this,g)?C(this,g):(e(this,g,A),A)}})}(),function(){if(xx)return Rx;xx=1;var g=st(),A=Zg(),t=mx(),C=lA(),I=t.get,e=t.has,i=t.set;g({target:"Map",proto:!0,real:!0,forced:C},{getOrInsertComputed:function(g,t){var C=e(this,g);if(A(t),C)return I(this,g);0===g&&1/g==-1/0&&(g=0);var o=t(g);return i(this,g,o),o}})}(),In(),Ex=Tg().Map)}function Mx(){if(Dx)return Tx;Dx=1;var g=Px();return oo(),Tx=g}var zx,Bx,Zx=C(kx?Nx:(kx=1,Nx=Mx())),Sx={};function Fx(){return Bx||(Bx=1,zx||(zx=1,sx()("Set",function(g){return function(){return g(this,arguments.length?arguments[0]:void 0)}},dx()))),Sx}var Gx,jx,Lx,Vx,Yx,Wx,Qx,Ux,Kx,Hx,Xx,_x,Jx,qx,$x,gE,AE,tE,CE,IE,eE,iE={};function oE(){if(jx)return Gx;jx=1;var g=Bg(),A=TypeError;return Gx=function(t){if("object"==typeof t&&"size"in t&&"has"in t&&"add"in t&&"delete"in t&&"keys"in t)return t;throw new A(g(t)+" is not a set")},Gx}function nE(){if(Vx)return Lx;Vx=1;var g=Dg(),A=bx(),t=g("Set"),C=t.prototype;return Lx={Set:t,add:A("add",1),has:A("has",1),remove:A("delete",1),proto:C}}function sE(){if(Wx)return Yx;Wx=1;var g=B();return Yx=function(A,t,C){for(var I,e,i=C?A:A.iterator,o=A.next;!(I=g(o,i)).done;)if(void 0!==(e=t(I.value)))return e},Yx}function rE(){if(Ux)return Qx;Ux=1;var g=sE();return Qx=function(A,t,C){return C?g(A.keys(),t,!0):A.forEach(t)},Qx}function aE(){if(Hx)return Kx;Hx=1;var g=nE(),A=rE(),t=g.Set,C=g.add;return Kx=function(g){var I=new t;return A(g,function(g){C(I,g)}),I},Kx}function dE(){return _x||(_x=1,Xx=function(g){return g.size}),Xx}function hE(){return qx?Jx:(qx=1,Jx=function(g){return{iterator:g,next:g.next,done:!1}})}function lE(){if(gE)return $x;gE=1;var g=Zg(),A=it(),t=B(),C=at(),I=hE(),e="Invalid size",i=RangeError,o=TypeError,n=Math.max,s=function(A,t){this.set=A,this.size=n(t,0),this.has=g(A.has),this.keys=g(A.keys)};return s.prototype={getIterator:function(){return I(A(t(this.keys,this.set)))},includes:function(g){return t(this.has,this.set,g)}},$x=function(g){A(g);var t=+g.size;if(t!=t)throw new o(e);var I=C(t);if(I<0)throw new i(e);return new s(g,I)}}function cE(){if(tE)return AE;tE=1;var g=oE(),A=nE(),t=aE(),C=dE(),I=lE(),e=rE(),i=sE(),o=A.has,n=A.remove;return AE=function(A){var s=g(this),r=I(A),a=t(s);return C(a)<=r.size?e(a,function(g){r.includes(g)&&n(a,g)}):i(r.getIterator(),function(g){o(a,g)&&n(a,g)}),a}}function uE(){return IE?CE:(IE=1,CE=function(){return!1})}var pE,fE,vE,bE={};function mE(){if(fE)return pE;fE=1;var g=oE(),A=nE(),t=dE(),C=lE(),I=rE(),e=sE(),i=A.Set,o=A.add,n=A.has;return pE=function(A){var s=g(this),r=C(A),a=new i;return t(s)>r.size?e(r.getIterator(),function(g){n(s,g)&&o(a,g)}):I(s,function(g){r.includes(g)&&o(a,g)}),a}}var yE,wE,xE,EE={};function OE(){if(wE)return yE;wE=1;var g=oE(),A=nE().has,t=dE(),C=lE(),I=rE(),e=sE(),i=Es();return yE=function(o){var n=g(this),s=C(o);if(t(n)<=s.size)return!1!==I(n,function(g){if(s.includes(g))return!1},!0);var r=s.getIterator();return!1!==e(r,function(g){if(A(n,g))return i(r.iterator,"normal",!1)})},yE}var TE,DE,NE,kE={};function RE(){if(DE)return TE;DE=1;var g=oE(),A=dE(),t=rE(),C=lE();return TE=function(I){var e=g(this),i=C(I);return!(A(e)>i.size)&&!1!==t(e,function(g){if(!i.includes(g))return!1},!0)}}var PE,ME,zE,BE={};function ZE(){if(ME)return PE;ME=1;var g=oE(),A=nE().has,t=dE(),C=lE(),I=sE(),e=Es();return PE=function(i){var o=g(this),n=C(i);if(t(o)1;return A.has(1)&&A.clear(),{done:t,value:2}}}}},A=new Set([1,2,3,4]);return 3!==A.difference(g).size});g({target:"Set",proto:!0,real:!0,forced:C},{difference:A})}(),function(){if(vE)return bE;vE=1;var g=st(),A=y(),t=mE();g({target:"Set",proto:!0,real:!0,forced:!uE()("intersection",function(g){return 2===g.size&&g.has(1)&&g.has(2)})||A(function(){return"3,2"!==String(Array.from(new Set([1,2,3]).intersection(new Set([3,2]))))})},{intersection:t})}(),function(){if(xE)return EE;xE=1;var g=st(),A=OE();g({target:"Set",proto:!0,real:!0,forced:!uE()("isDisjointFrom",function(g){return!g})},{isDisjointFrom:A})}(),function(){if(NE)return kE;NE=1;var g=st(),A=RE();g({target:"Set",proto:!0,real:!0,forced:!uE()("isSubsetOf",function(g){return g})},{isSubsetOf:A})}(),function(){if(zE)return BE;zE=1;var g=st(),A=ZE();g({target:"Set",proto:!0,real:!0,forced:!uE()("isSupersetOf",function(g){return!g})},{isSupersetOf:A})}(),function(){if(LE)return VE;LE=1;var g=st(),A=YE(),t=WE();g({target:"Set",proto:!0,real:!0,forced:!uE()("symmetricDifference")||!t("symmetricDifference")},{symmetricDifference:A})}(),function(){if(KE)return gO;KE=1;var g=st(),A=AO(),t=WE();g({target:"Set",proto:!0,real:!0,forced:!uE()("union")||!t("union")},{union:A})}(),In(),HE=Tg().Set)}function CO(){if(JE)return _E;JE=1;var g=tO();return oo(),_E=g}var IO,eO,iO,oO,nO,sO,rO,aO,dO,hO,lO,cO,uO=C($E?qE:($E=1,qE=CO()));function pO(){return eO?IO:(eO=1,eo(),In(),IO=Ds())}function fO(){if(oO)return iO;oO=1;var g=pO();return oo(),iO=g}function vO(){return sO?nO:(sO=1,nO=fO())}function bO(){return aO?rO:(aO=1,rO=vO())}function mO(){return hO?dO:(hO=1,dO=bO())}var yO,wO,xO,EO,OO,TO,DO,NO,kO,RO,PO,MO,zO,BO,ZO,SO,FO,GO=C(cO?lO:(cO=1,lO=mO())),jO={};function LO(){if(wO)return yO;wO=1;var g=qt(),A=Math.floor,t=function(C,I){var e=C.length;if(e<8)for(var i,o,n=1;n0;)C[o]=C[--o];o!==n++&&(C[o]=i)}else for(var s=A(e/2),r=t(g(C,0,s),I),a=t(g(C,s),I),d=r.length,h=a.length,l=0,c=0;l3)){if(a)return!0;if(h)return h<603;var g,A,t,C,I="";for(g=65;g<76;g++){switch(A=String.fromCharCode(g),g){case 66:case 69:case 70:case 72:t=3;break;case 68:case 71:t=4;break;default:t=2}for(C=0;C<47;C++)l.push({k:A+C,v:t})}for(l.sort(function(g,A){return A.v-g.v}),C=0;CI?1:-1}}(g)),o=I(r),s=0;s1?arguments[1]:void 0)}})}(),_O=gC()("Array","some"))}function oT(){if($O)return qO;$O=1;var g=Ng(),A=iT(),t=Array.prototype;return qO=function(C){var I=C.some;return C===t||g(t,C)&&I===t.some?A:I},qO}function nT(){return AT?gT:(AT=1,gT=oT())}var sT,rT,aT,dT,hT,lT,cT,uT,pT=C(CT?tT:(CT=1,tT=nT()));function fT(){return rT?sT:(rT=1,eo(),sT=gC()("Array","keys"))}function vT(){return dT?aT:(dT=1,aT=fT())}function bT(){if(lT)return hT;lT=1,oo();var g=oI(),A=vA(),t=Ng(),C=vT(),I=Array.prototype,e={DOMTokenList:!0,NodeList:!0};return hT=function(i){var o=i.keys;return i===I||t(I,i)&&o===I.keys||A(e,g(i))?C:o},hT}var mT,yT,wT,xT,ET,OT,TT,DT,NT=C(uT?cT:(uT=1,cT=bT()));function kT(){return yT?mT:(yT=1,eo(),mT=gC()("Array","entries"))}function RT(){return xT?wT:(xT=1,wT=kT())}function PT(){if(OT)return ET;OT=1,oo();var g=oI(),A=vA(),t=Ng(),C=RT(),I=Array.prototype,e={DOMTokenList:!0,NodeList:!0};return ET=function(i){var o=i.entries;return i===I||t(I,i)&&o===I.entries||A(e,g(i))?C:o},ET}var MT=C(DT?TT:(DT=1,TT=PT()));function zT(g,A){var t=Fr(g);if(Ob){var C=Ob(g);A&&(C=eh(C).call(C,function(A){return Fb(g,A).enumerable})),t.push.apply(t,C)}return t}function BT(g){for(var A=1;AA(g),g)}_add(g,A){null!=A&&this._target.add(this._transformItems(this._source.get(A.items)))}_update(g,A){null!=A&&this._target.update(this._transformItems(this._source.get(A.items)))}_remove(g,A){null!=A&&this._target.remove(this._transformItems(A.oldData))}}class ST{constructor(g){pb(this,"_source",void 0),pb(this,"_transformers",[]),this._source=g}filter(g){return this._transformers.push(A=>eh(A).call(A,g)),this}map(g){return this._transformers.push(A=>Tr(A).call(A,g)),this}flatMap(g){return this._transformers.push(A=>Pw(A).call(A,g)),this}to(g){return new ZT(this._source,this._transformers,g)}}function FT(g){return"string"==typeof g||"number"==typeof g}class GT{constructor(g){pb(this,"delay",void 0),pb(this,"max",void 0),pb(this,"_queue",[]),pb(this,"_timeout",null),pb(this,"_extended",null),this.delay=null,this.max=1/0,this.setOptions(g)}setOptions(g){g&&void 0!==g.delay&&(this.delay=g.delay),g&&void 0!==g.max&&(this.max=g.max),this._flushIfNeeded()}static extend(g,A){const t=new GT(A);if(void 0!==g.flush)throw new Error("Target object already has a property flush");g.flush=()=>{t.flush()};const C=[{name:"flush",original:void 0}];if(A&&A.replace)for(let I=0;Ithis.max&&this.flush(),null!=this._timeout&&(clearTimeout(this._timeout),this._timeout=null),this.queue.length>0&&"number"==typeof this.delay&&(this._timeout=$l(()=>{this.flush()},this.delay))}flush(){var g,A;pa(g=Va(A=this._queue).call(A,0)).call(g,g=>{g.fn.apply(g.context||g.fn,g.args||[])})}}class jT{constructor(){pb(this,"_subscribers",{"*":[],add:[],remove:[],update:[]}),pb(this,"subscribe",jT.prototype.on),pb(this,"unsubscribe",jT.prototype.off)}_trigger(g,A,t){var C;if("*"===g)throw new Error("Cannot trigger event *");pa(C=[...this._subscribers[g],...this._subscribers["*"]]).call(C,C=>{C(g,A,null!=t?t:null)})}on(g,A){"function"==typeof A&&this._subscribers[g].push(A)}off(g,A){var t;this._subscribers[g]=eh(t=this._subscribers[g]).call(t,g=>g!==A)}}class LT{constructor(g){pb(this,"_pairs",void 0),this._pairs=g}*[gx](){for(const A of this._pairs){var g=js(A,2);const t=g[0],C=g[1];yield[t,C]}}*entries(){for(const A of this._pairs){var g=js(A,2);const t=g[0],C=g[1];yield[t,C]}}*keys(){for(const g of this._pairs){const A=js(g,1)[0];yield A}}*values(){for(const g of this._pairs){const A=js(g,2)[1];yield A}}toIdArray(){var g;return Tr(g=[...this._pairs]).call(g,g=>g[0])}toItemArray(){var g;return Tr(g=[...this._pairs]).call(g,g=>g[1])}toEntryArray(){return[...this._pairs]}toObjectMap(){const g=hl(null);for(const t of this._pairs){var A=js(t,2);const C=A[0],I=A[1];g[C]=I}return g}toMap(){return new Zx(this._pairs)}toIdSet(){return new uO(this.toIdArray())}toItemSet(){return new uO(this.toItemArray())}cache(){return new LT([...this._pairs])}distinct(g){const A=new uO;for(const C of this._pairs){var t=js(C,2);const I=t[0],e=t[1];A.add(g(e,I))}return A}filter(g){const A=this._pairs;return new LT({*[gx](){for(const C of A){var t=js(C,2);const A=t[0],I=t[1];g(I,A)&&(yield[A,I])}}})}forEach(g){for(const t of this._pairs){var A=js(t,2);const C=A[0];g(A[1],C)}}map(g){const A=this._pairs;return new LT({*[gx](){for(const C of A){var t=js(C,2);const A=t[0],I=t[1];yield[A,g(I,A)]}}})}max(g){const A=GO(this._pairs);let t=A.next();if(t.done)return null;let C=t.value[1],I=g(t.value[1],t.value[0]);for(;!(t=A.next()).done;){const A=js(t.value,2),e=A[0],i=A[1],o=g(i,e);o>I&&(I=o,C=i)}return C}min(g){const A=GO(this._pairs);let t=A.next();if(t.done)return null;let C=t.value[1],I=g(t.value[1],t.value[0]);for(;!(t=A.next()).done;){const A=js(t.value,2),e=A[0],i=A[1],o=g(i,e);o{var A;return GO(IT(A=[...this._pairs]).call(A,(A,t)=>{let C=js(A,2),I=C[0],e=C[1],i=js(t,2),o=i[0],n=i[1];return g(e,n,I,o)}))}})}}class VT extends jT{get idProp(){return this._idProp}constructor(g,A){super(),pb(this,"flush",void 0),pb(this,"length",void 0),pb(this,"_options",void 0),pb(this,"_data",void 0),pb(this,"_idProp",void 0),pb(this,"_queue",null),g&&!cr(g)&&(A=g,g=[]),this._options=A||{},this._data=new Zx,this.length=0,this._idProp=this._options.fieldId||"id",g&&g.length&&this.add(g),this.setOptions(A)}setOptions(g){g&&void 0!==g.queue&&(!1===g.queue?this._queue&&(this._queue.destroy(),this._queue=null):(this._queue||(this._queue=GT.extend(this,{replace:["add","update","remove"]})),g.queue&&"object"==typeof g.queue&&this._queue.setOptions(g.queue)))}add(g,A){const t=[];let C;if(cr(g)){const A=Tr(g).call(g,g=>g[this._idProp]);if(pT(A).call(A,g=>this._data.has(g)))throw new Error("A duplicate id was found in the parameter array.");for(let A=0,I=g.length;A{const A=g[i];if(null!=A&&this._data.has(A)){const t=g,i=_t({},this._data.get(A)),o=this._updateItem(t);C.push(o),e.push(t),I.push(i)}else{const A=this._addItem(g);t.push(A)}};if(cr(g))for(let A=0,t=g.length;A{const A=this._data.get(g[this._idProp]);if(null==A)throw new Error("Updating non-existent items is not allowed.");return{oldData:A,update:g}})).call(t,g=>{let A=g.oldData,t=g.update;const C=A[this._idProp],I=function(g){for(var A=arguments.length,t=new Array(A>1?A-1:0),C=1;Cg.id),oldData:Tr(C).call(C,g=>g.oldData),data:Tr(C).call(C,g=>g.updatedData)};return this._trigger("update",g,A),g.items}return[]}get(g,A){let t,C,I;FT(g)?(t=g,I=A):cr(g)?(C=g,I=A):I=g;const e=I&&"Object"===I.returnType?"Object":"Array",i=I&&eh(I),o=[];let n,s,r;if(null!=t)n=this._data.get(t),n&&i&&!i(n)&&(n=void 0);else if(null!=C)for(let g=0,A=C.length;g(A[t]=g[t],A),{}):g}_sort(g,A){if("string"==typeof A){const t=A;IT(g).call(g,(g,A)=>{const C=g[t],I=A[t];return C>I?1:Ct)&&(A=I,t=e)}return A||null}min(g){let A=null,t=null;for(const I of Ay(C=this._data).call(C)){var C;const e=I[g];"number"==typeof e&&(null==t||ee(g)&&i(g)),null==C?this._data.get(I):this._data.get(C,I)}getIds(g){if(this._data.length){const A=eh(this._options),t=null!=g?eh(g):null;let C;return C=t?A?g=>A(g)&&t(g):t:A,this._data.getIds({filter:C,order:g&&g.order})}return[]}forEach(g,A){if(this._data){var t;const C=eh(this._options),I=A&&eh(A);let e;e=I?C?function(g){return C(g)&&I(g)}:I:C,pa(t=this._data).call(t,g,{filter:e,order:A&&A.order})}}map(g,A){if(this._data){var t;const C=eh(this._options),I=A&&eh(A);let e;return e=I?C?g=>C(g)&&I(g):I:C,Tr(t=this._data).call(t,g,{filter:e,order:A&&A.order})}return[]}getDataSet(){return this._data.getDataSet()}stream(g){var A;return this._data.stream(g||{[gx]:IC(A=NT(this._ids)).call(A,this._ids)})}dispose(){var g;null!==(g=this._data)&&void 0!==g&&g.off&&this._data.off("*",this._listener);const A="This data view has already been disposed of.",t={get:()=>{throw new Error(A)},set:()=>{throw new Error(A)},configurable:!1};for(const g of er(YT.prototype))rm(this,g,t)}_onEvent(g,A,t){if(!A||!A.items||!this._data)return;const C=A.items,I=[],e=[],i=[],o=[],n=[],s=[];switch(g){case"add":for(let g=0,A=C.length;g{this.add(A.items)},update:(g,A)=>{this.update(A.items)},remove:(g,A)=>{this.remove(A.items)}},this.options={},this.defaultOptions={arrows:{to:{enabled:!1,scaleFactor:1,type:"arrow"},middle:{enabled:!1,scaleFactor:1,type:"arrow"},from:{enabled:!1,scaleFactor:1,type:"arrow"}},endPointOffset:{from:0,to:0},arrowStrikethrough:!0,color:{color:"#848484",highlight:"#848484",hover:"#848484",inherit:"from",opacity:1},dashes:!1,font:{color:"#343434",size:14,face:"arial",background:"none",strokeWidth:2,strokeColor:"#ffffff",align:"horizontal",multi:!1,vadjust:0,bold:{mod:"bold"},boldital:{mod:"bold italic"},ital:{mod:"italic"},mono:{mod:"",size:15,face:"courier new",vadjust:2}},hidden:!1,hoverWidth:1.5,label:void 0,labelHighlightBold:!0,length:void 0,physics:!0,scaling:{min:1,max:15,label:{enabled:!0,min:14,max:30,maxVisible:30,drawThreshold:5},customScalingFunction:function(g,A,t,C){if(A===g)return.5;{const t=1/(A-g);return Math.max(0,(C-g)*t)}}},selectionWidth:1.5,selfReference:{size:20,angle:Math.PI/4,renderBehindTheNode:!0},shadow:{enabled:!1,color:"rgba(0,0,0,0.5)",size:10,x:5,y:5},background:{enabled:!1,color:"rgba(111,111,111,1)",size:10,dashes:!1},smooth:{enabled:!0,type:"dynamic",forceDirection:"none",roundness:.5},title:void 0,width:1,value:void 0},sp(this.options,this.defaultOptions),this.bindEventListeners()}bindEventListeners(){var g,A,t=this;this.body.emitter.on("_forceDisableDynamicCurves",function(g){let A=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];"dynamic"===g&&(g="continuous");let C=!1;for(const A in t.body.edges)if(Object.prototype.hasOwnProperty.call(t.body.edges,A)){const I=t.body.edges[A],e=t.body.data.edges.get(A);if(null!=e){const A=e.smooth;void 0!==A&&!0===A.enabled&&"dynamic"===A.type&&(void 0===g?I.setOptions({smooth:!1}):I.setOptions({smooth:{type:g}}),C=!0)}}!0===A&&!0===C&&t.body.emitter.emit("_dataChanged")}),this.body.emitter.on("_dataUpdated",()=>{this.reconnectEdges()}),this.body.emitter.on("refreshEdges",IC(g=this.refresh).call(g,this)),this.body.emitter.on("refresh",IC(A=this.refresh).call(A,this)),this.body.emitter.on("destroy",()=>{hp(this.edgesListeners,(g,A)=>{this.body.data.edges&&this.body.data.edges.off(A,g)}),delete this.body.functions.createEdge,delete this.edgesListeners.add,delete this.edgesListeners.update,delete this.edgesListeners.remove,delete this.edgesListeners})}setOptions(g){if(void 0!==g){ny.parseOptions(this.options,g,!0,this.defaultOptions,!0);let A=!1;if(void 0!==g.smooth)for(const g in this.body.edges)Object.prototype.hasOwnProperty.call(this.body.edges,g)&&(A=this.body.edges[g].updateEdgeType()||A);if(void 0!==g.font)for(const g in this.body.edges)Object.prototype.hasOwnProperty.call(this.body.edges,g)&&this.body.edges[g].updateLabelModule();void 0===g.hidden&&void 0===g.physics&&!0!==A||this.body.emitter.emit("_dataChanged")}}setData(g){let A=arguments.length>1&&void 0!==arguments[1]&&arguments[1];const t=this.body.data.edges;if(QT("id",g))this.body.data.edges=g;else if(cr(g))this.body.data.edges=new VT,this.body.data.edges.add(g);else{if(g)throw new TypeError("Array or DataSet expected");this.body.data.edges=new VT}if(t&&hp(this.edgesListeners,(g,A)=>{t.off(A,g)}),this.body.edges={},this.body.data.edges){hp(this.edgesListeners,(g,A)=>{this.body.data.edges.on(A,g)});const g=this.body.data.edges.getIds();this.add(g,!0)}this.body.emitter.emit("_adjustEdgesForHierarchicalLayout"),!1===A&&this.body.emitter.emit("_dataChanged")}add(g){let A=arguments.length>1&&void 0!==arguments[1]&&arguments[1];const t=this.body.edges,C=this.body.data.edges;for(let A=0;A1&&void 0!==arguments[1])||arguments[1];if(0===g.length)return;const t=this.body.edges;hp(g,g=>{const A=t[g];void 0!==A&&A.remove()}),A&&this.body.emitter.emit("_dataChanged")}refresh(){hp(this.body.edges,(g,A)=>{const t=this.body.data.edges.get(A);void 0!==t&&g.setOptions(t)})}create(g){return new ny(g,this.body,this.images,this.options,this.defaultOptions)}reconnectEdges(){let g;const A=this.body.nodes,t=this.body.edges;for(g in A)Object.prototype.hasOwnProperty.call(A,g)&&(A[g].edges=[]);for(g in t)if(Object.prototype.hasOwnProperty.call(t,g)){const A=t[g];A.from=null,A.to=null,A.connect()}}getConnectedNodes(g){const A=[];if(void 0!==this.body.edges[g]){const t=this.body.edges[g];void 0!==t.fromId&&A.push(t.fromId),void 0!==t.toId&&A.push(t.toId)}return A}_updateState(){this._addMissingEdges(),this._removeInvalidEdges()}_removeInvalidEdges(){const g=[];hp(this.body.edges,(A,t)=>{const C=this.body.nodes[A.toId],I=this.body.nodes[A.fromId];void 0!==C&&!0===C.isCluster||void 0!==I&&!0===I.isCluster||void 0!==C&&void 0!==I||g.push(t)}),this.remove(g,!1)}_addMissingEdges(){const g=this.body.data.edges;if(null==g)return;const A=this.body.edges,t=[];pa(g).call(g,(g,C)=>{void 0===A[C]&&t.push(C)}),this.add(t,!0)}}class HT{constructor(){this.clear(),this._defaultIndex=0,this._groupIndex=0,this._defaultGroups=[{border:"#2B7CE9",background:"#97C2FC",highlight:{border:"#2B7CE9",background:"#D2E5FF"},hover:{border:"#2B7CE9",background:"#D2E5FF"}},{border:"#FFA500",background:"#FFFF00",highlight:{border:"#FFA500",background:"#FFFFA3"},hover:{border:"#FFA500",background:"#FFFFA3"}},{border:"#FA0A10",background:"#FB7E81",highlight:{border:"#FA0A10",background:"#FFAFB1"},hover:{border:"#FA0A10",background:"#FFAFB1"}},{border:"#41A906",background:"#7BE141",highlight:{border:"#41A906",background:"#A1EC76"},hover:{border:"#41A906",background:"#A1EC76"}},{border:"#E129F0",background:"#EB7DF4",highlight:{border:"#E129F0",background:"#F0B3F5"},hover:{border:"#E129F0",background:"#F0B3F5"}},{border:"#7C29F0",background:"#AD85E4",highlight:{border:"#7C29F0",background:"#D3BDF0"},hover:{border:"#7C29F0",background:"#D3BDF0"}},{border:"#C37F00",background:"#FFA807",highlight:{border:"#C37F00",background:"#FFCA66"},hover:{border:"#C37F00",background:"#FFCA66"}},{border:"#4220FB",background:"#6E6EFD",highlight:{border:"#4220FB",background:"#9B9BFD"},hover:{border:"#4220FB",background:"#9B9BFD"}},{border:"#FD5A77",background:"#FFC0CB",highlight:{border:"#FD5A77",background:"#FFD1D9"},hover:{border:"#FD5A77",background:"#FFD1D9"}},{border:"#4AD63A",background:"#C2FABC",highlight:{border:"#4AD63A",background:"#E6FFE3"},hover:{border:"#4AD63A",background:"#E6FFE3"}},{border:"#990000",background:"#EE0000",highlight:{border:"#BB0000",background:"#FF3333"},hover:{border:"#BB0000",background:"#FF3333"}},{border:"#FF6000",background:"#FF6000",highlight:{border:"#FF6000",background:"#FF6000"},hover:{border:"#FF6000",background:"#FF6000"}},{border:"#97C2FC",background:"#2B7CE9",highlight:{border:"#D2E5FF",background:"#2B7CE9"},hover:{border:"#D2E5FF",background:"#2B7CE9"}},{border:"#399605",background:"#255C03",highlight:{border:"#399605",background:"#255C03"},hover:{border:"#399605",background:"#255C03"}},{border:"#B70054",background:"#FF007E",highlight:{border:"#B70054",background:"#FF007E"},hover:{border:"#B70054",background:"#FF007E"}},{border:"#AD85E4",background:"#7C29F0",highlight:{border:"#D3BDF0",background:"#7C29F0"},hover:{border:"#D3BDF0",background:"#7C29F0"}},{border:"#4557FA",background:"#000EA1",highlight:{border:"#6E6EFD",background:"#000EA1"},hover:{border:"#6E6EFD",background:"#000EA1"}},{border:"#FFC0CB",background:"#FD5A77",highlight:{border:"#FFD1D9",background:"#FD5A77"},hover:{border:"#FFD1D9",background:"#FD5A77"}},{border:"#C2FABC",background:"#74D66A",highlight:{border:"#E6FFE3",background:"#74D66A"},hover:{border:"#E6FFE3",background:"#74D66A"}},{border:"#EE0000",background:"#990000",highlight:{border:"#FF3333",background:"#BB0000"},hover:{border:"#FF3333",background:"#BB0000"}}],this.options={},this.defaultOptions={useDefaultGroups:!0},_t(this.options,this.defaultOptions)}setOptions(g){const A=["useDefaultGroups"];if(void 0!==g)for(const t in g)if(Object.prototype.hasOwnProperty.call(g,t)&&-1===Jh(A).call(A,t)){const A=g[t];this.add(t,A)}}clear(){this._groups=new Zx,this._groupNames=[]}get(g){let A=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],t=this._groups.get(g);if(void 0===t&&A)if(!1===this.options.useDefaultGroups&&this._groupNames.length>0){const A=this._groupIndex%this._groupNames.length;++this._groupIndex,t={},t.color=this._groups.get(this._groupNames[A]),this._groups.set(g,t)}else{const A=this._defaultIndex%this._defaultGroups.length;this._defaultIndex++,t={},t.color=this._defaultGroups[A],this._groups.set(g,t)}return t}add(g,A){return this._groups.has(g)||this._groupNames.push(g),this._groups.set(g,A),A}}function XT(g){var A,t=g&&g.preventDefault||!1,C=g&&g.container||window,I={},e={keydown:{},keyup:{}},i={};for(A=97;A<=122;A++)i[String.fromCharCode(A)]={code:A-97+65,shift:!1};for(A=65;A<=90;A++)i[String.fromCharCode(A)]={code:A,shift:!0};for(A=0;A<=9;A++)i[""+A]={code:48+A,shift:!1};for(A=1;A<=12;A++)i["F"+A]={code:111+A,shift:!1};for(A=0;A<=9;A++)i["num"+A]={code:96+A,shift:!1};i["num*"]={code:106,shift:!1},i["num+"]={code:107,shift:!1},i["num-"]={code:109,shift:!1},i["num/"]={code:111,shift:!1},i["num."]={code:110,shift:!1},i.left={code:37,shift:!1},i.up={code:38,shift:!1},i.right={code:39,shift:!1},i.down={code:40,shift:!1},i.space={code:32,shift:!1},i.enter={code:13,shift:!1},i.shift={code:16,shift:void 0},i.esc={code:27,shift:!1},i.backspace={code:8,shift:!1},i.tab={code:9,shift:!1},i.ctrl={code:17,shift:!1},i.alt={code:18,shift:!1},i.delete={code:46,shift:!1},i.pageup={code:33,shift:!1},i.pagedown={code:34,shift:!1},i["="]={code:187,shift:!1},i["-"]={code:189,shift:!1},i["]"]={code:221,shift:!1},i["["]={code:219,shift:!1};var o=function(g){s(g,"keydown")},n=function(g){s(g,"keyup")},s=function(g,A){if(void 0!==e[A][g.keyCode]){for(var C=e[A][g.keyCode],I=0;I{this.activated=!0,this.configureKeyboardBindings()}),this.body.emitter.on("deactivate",()=>{this.activated=!1,this.configureKeyboardBindings()}),this.body.emitter.on("destroy",()=>{void 0!==this.keycharm&&this.keycharm.destroy()}),this.options={}}setOptions(g){void 0!==g&&(this.options=g,this.create())}create(){!0===this.options.navigationButtons?!1===this.iconsCreated&&this.loadNavigationElements():!0===this.iconsCreated&&this.cleanNavigation(),this.configureKeyboardBindings()}cleanNavigation(){if(0!=this.navigationHammers.length){for(let g=0;g{this._stopMovement()}),this.navigationHammers.push(I),this.iconsCreated=!0}bindToRedraw(g){var A;void 0===this.boundFunctions[g]&&(this.boundFunctions[g]=IC(A=this[g]).call(A,this),this.body.emitter.on("initRedraw",this.boundFunctions[g]),this.body.emitter.emit("_startRendering"))}unbindFromRedraw(g){void 0!==this.boundFunctions[g]&&(this.body.emitter.off("initRedraw",this.boundFunctions[g]),this.body.emitter.emit("_stopRendering"),delete this.boundFunctions[g])}_fit(){(new Date).valueOf()-this.touchTime>700&&(this.body.emitter.emit("fit",{duration:700}),this.touchTime=(new Date).valueOf())}_stopMovement(){for(const g in this.boundFunctions)Object.prototype.hasOwnProperty.call(this.boundFunctions,g)&&(this.body.emitter.off("initRedraw",this.boundFunctions[g]),this.body.emitter.emit("_stopRendering"));this.boundFunctions={}}_moveUp(){this.body.view.translation.y+=this.options.keyboard.speed.y}_moveDown(){this.body.view.translation.y-=this.options.keyboard.speed.y}_moveLeft(){this.body.view.translation.x+=this.options.keyboard.speed.x}_moveRight(){this.body.view.translation.x-=this.options.keyboard.speed.x}_zoomIn(){const g=this.body.view.scale,A=this.body.view.scale*(1+this.options.keyboard.speed.zoom),t=this.body.view.translation,C=A/g,I=(1-C)*this.canvas.canvasViewCenter.x+t.x*C,e=(1-C)*this.canvas.canvasViewCenter.y+t.y*C;this.body.view.scale=A,this.body.view.translation={x:I,y:e},this.body.emitter.emit("zoom",{direction:"+",scale:this.body.view.scale,pointer:null})}_zoomOut(){const g=this.body.view.scale,A=this.body.view.scale/(1+this.options.keyboard.speed.zoom),t=this.body.view.translation,C=A/g,I=(1-C)*this.canvas.canvasViewCenter.x+t.x*C,e=(1-C)*this.canvas.canvasViewCenter.y+t.y*C;this.body.view.scale=A,this.body.view.translation={x:I,y:e},this.body.emitter.emit("zoom",{direction:"-",scale:this.body.view.scale,pointer:null})}configureKeyboardBindings(){var g,A,t,C,I,e,i,o,n,s,r,a,d,h,l,c,u,p,f,v,b,m,y,w;(void 0!==this.keycharm&&this.keycharm.destroy(),!0===this.options.keyboard.enabled)&&(!0===this.options.keyboard.bindToWindow?this.keycharm=XT({container:window,preventDefault:!0}):this.keycharm=XT({container:this.canvas.frame,preventDefault:!0}),this.keycharm.reset(),!0===this.activated&&(IC(g=this.keycharm).call(g,"up",()=>{this.bindToRedraw("_moveUp")},"keydown"),IC(A=this.keycharm).call(A,"down",()=>{this.bindToRedraw("_moveDown")},"keydown"),IC(t=this.keycharm).call(t,"left",()=>{this.bindToRedraw("_moveLeft")},"keydown"),IC(C=this.keycharm).call(C,"right",()=>{this.bindToRedraw("_moveRight")},"keydown"),IC(I=this.keycharm).call(I,"=",()=>{this.bindToRedraw("_zoomIn")},"keydown"),IC(e=this.keycharm).call(e,"num+",()=>{this.bindToRedraw("_zoomIn")},"keydown"),IC(i=this.keycharm).call(i,"num-",()=>{this.bindToRedraw("_zoomOut")},"keydown"),IC(o=this.keycharm).call(o,"-",()=>{this.bindToRedraw("_zoomOut")},"keydown"),IC(n=this.keycharm).call(n,"[",()=>{this.bindToRedraw("_zoomOut")},"keydown"),IC(s=this.keycharm).call(s,"]",()=>{this.bindToRedraw("_zoomIn")},"keydown"),IC(r=this.keycharm).call(r,"pageup",()=>{this.bindToRedraw("_zoomIn")},"keydown"),IC(a=this.keycharm).call(a,"pagedown",()=>{this.bindToRedraw("_zoomOut")},"keydown"),IC(d=this.keycharm).call(d,"up",()=>{this.unbindFromRedraw("_moveUp")},"keyup"),IC(h=this.keycharm).call(h,"down",()=>{this.unbindFromRedraw("_moveDown")},"keyup"),IC(l=this.keycharm).call(l,"left",()=>{this.unbindFromRedraw("_moveLeft")},"keyup"),IC(c=this.keycharm).call(c,"right",()=>{this.unbindFromRedraw("_moveRight")},"keyup"),IC(u=this.keycharm).call(u,"=",()=>{this.unbindFromRedraw("_zoomIn")},"keyup"),IC(p=this.keycharm).call(p,"num+",()=>{this.unbindFromRedraw("_zoomIn")},"keyup"),IC(f=this.keycharm).call(f,"num-",()=>{this.unbindFromRedraw("_zoomOut")},"keyup"),IC(v=this.keycharm).call(v,"-",()=>{this.unbindFromRedraw("_zoomOut")},"keyup"),IC(b=this.keycharm).call(b,"[",()=>{this.unbindFromRedraw("_zoomOut")},"keyup"),IC(m=this.keycharm).call(m,"]",()=>{this.unbindFromRedraw("_zoomIn")},"keyup"),IC(y=this.keycharm).call(y,"pageup",()=>{this.unbindFromRedraw("_zoomIn")},"keyup"),IC(w=this.keycharm).call(w,"pagedown",()=>{this.unbindFromRedraw("_zoomOut")},"keyup")))}}class JT{constructor(g,A,t){var C,I,e,i,o,n,s,r,a,d,h,l,c;this.body=g,this.canvas=A,this.selectionHandler=t,this.navigationHandler=new _T(g,A),this.body.eventListeners.onTap=IC(C=this.onTap).call(C,this),this.body.eventListeners.onTouch=IC(I=this.onTouch).call(I,this),this.body.eventListeners.onDoubleTap=IC(e=this.onDoubleTap).call(e,this),this.body.eventListeners.onHold=IC(i=this.onHold).call(i,this),this.body.eventListeners.onDragStart=IC(o=this.onDragStart).call(o,this),this.body.eventListeners.onDrag=IC(n=this.onDrag).call(n,this),this.body.eventListeners.onDragEnd=IC(s=this.onDragEnd).call(s,this),this.body.eventListeners.onMouseWheel=IC(r=this.onMouseWheel).call(r,this),this.body.eventListeners.onPinch=IC(a=this.onPinch).call(a,this),this.body.eventListeners.onMouseMove=IC(d=this.onMouseMove).call(d,this),this.body.eventListeners.onRelease=IC(h=this.onRelease).call(h,this),this.body.eventListeners.onContext=IC(l=this.onContext).call(l,this),this.touchTime=0,this.drag={},this.pinch={},this.popup=void 0,this.popupObj=void 0,this.popupTimer=void 0,this.body.functions.getPointer=IC(c=this.getPointer).call(c,this),this.options={},this.defaultOptions={dragNodes:!0,dragView:!0,hover:!1,keyboard:{enabled:!1,speed:{x:10,y:10,zoom:.02},bindToWindow:!0,autoFocus:!0},navigationButtons:!1,tooltipDelay:300,zoomView:!0,zoomSpeed:1},_t(this.options,this.defaultOptions),this.bindEventListeners()}bindEventListeners(){this.body.emitter.on("destroy",()=>{clearTimeout(this.popupTimer),delete this.body.functions.getPointer})}setOptions(g){if(void 0!==g){np(["hideEdgesOnDrag","hideEdgesOnZoom","hideNodesOnDrag","keyboard","multiselect","selectable","selectConnectedEdges"],this.options,g),xp(this.options,g,"keyboard"),g.tooltip&&(_t(this.options.tooltip,g.tooltip),g.tooltip.color&&(this.options.tooltip.color=pp(g.tooltip.color)))}this.navigationHandler.setOptions(this.options)}getPointer(g){return{x:g.x-(A=this.canvas.frame.canvas,A.getBoundingClientRect().left),y:g.y-dp(this.canvas.frame.canvas)};var A}onTouch(g){(new Date).valueOf()-this.touchTime>50&&(this.drag.pointer=this.getPointer(g.center),this.drag.pinched=!1,this.pinch.scale=this.body.view.scale,this.touchTime=(new Date).valueOf())}onTap(g){const A=this.getPointer(g.center),t=this.selectionHandler.options.multiselect&&(g.changedPointers[0].ctrlKey||g.changedPointers[0].metaKey);this.checkSelectionChanges(A,t),this.selectionHandler.commitAndEmit(A,g),this.selectionHandler.generateClickEvent("click",g,A)}onDoubleTap(g){const A=this.getPointer(g.center);this.selectionHandler.generateClickEvent("doubleClick",g,A)}onHold(g){const A=this.getPointer(g.center),t=this.selectionHandler.options.multiselect;this.checkSelectionChanges(A,t),this.selectionHandler.commitAndEmit(A,g),this.selectionHandler.generateClickEvent("click",g,A),this.selectionHandler.generateClickEvent("hold",g,A)}onRelease(g){if((new Date).valueOf()-this.touchTime>10){const A=this.getPointer(g.center);this.selectionHandler.generateClickEvent("release",g,A),this.touchTime=(new Date).valueOf()}}onContext(g){const A=this.getPointer({x:g.clientX,y:g.clientY});this.selectionHandler.generateClickEvent("oncontext",g,A)}checkSelectionChanges(g){!0===(arguments.length>1&&void 0!==arguments[1]&&arguments[1])?this.selectionHandler.selectAdditionalOnPoint(g):this.selectionHandler.selectOnPoint(g)}_determineDifference(g,A){const t=function(g,A){const t=[];for(let C=0;C{const A=g.node;!1===g.xFixed&&(A.x=this.canvas._XconvertDOMtoCanvas(this.canvas._XconvertCanvasToDOM(g.x)+C)),!1===g.yFixed&&(A.y=this.canvas._YconvertDOMtoCanvas(this.canvas._YconvertCanvasToDOM(g.y)+I))}),this.body.emitter.emit("startSimulation")}else{if(g.srcEvent.shiftKey){if(this.selectionHandler.generateClickEvent("dragging",g,A,void 0,!0),void 0===this.drag.pointer)return void this.onDragStart(g);this.body.selectionBox.position.end={x:this.canvas._XconvertDOMtoCanvas(A.x),y:this.canvas._YconvertDOMtoCanvas(A.y)},this.body.emitter.emit("_requestRedraw")}if(!0===this.options.dragView&&!g.srcEvent.shiftKey){if(this.selectionHandler.generateClickEvent("dragging",g,A,void 0,!0),void 0===this.drag.pointer)return void this.onDragStart(g);const t=A.x-this.drag.pointer.x,C=A.y-this.drag.pointer.y;this.body.view.translation={x:this.drag.translation.x+t,y:this.drag.translation.y+C},this.body.emitter.emit("_requestRedraw")}}}onDragEnd(g){if(this.drag.dragging=!1,this.body.selectionBox.show){var A;this.body.selectionBox.show=!1;const t=this.body.selectionBox.position,C={minX:Math.min(t.start.x,t.end.x),minY:Math.min(t.start.y,t.end.y),maxX:Math.max(t.start.x,t.end.x),maxY:Math.max(t.start.y,t.end.y)},I=eh(A=this.body.nodeIndices).call(A,g=>{const A=this.body.nodes[g];return A.x>=C.minX&&A.x<=C.maxX&&A.y>=C.minY&&A.y<=C.maxY});pa(I).call(I,g=>this.selectionHandler.selectObject(this.body.nodes[g]));const e=this.getPointer(g.center);this.selectionHandler.commitAndEmit(e,g),this.selectionHandler.generateClickEvent("dragEnd",g,this.getPointer(g.center),void 0,!0),this.body.emitter.emit("_requestRedraw")}else{const A=this.drag.selection;A&&A.length?(pa(A).call(A,function(g){g.node.options.fixed.x=g.xFixed,g.node.options.fixed.y=g.yFixed}),this.selectionHandler.generateClickEvent("dragEnd",g,this.getPointer(g.center)),this.body.emitter.emit("startSimulation")):(this.selectionHandler.generateClickEvent("dragEnd",g,this.getPointer(g.center),void 0,!0),this.body.emitter.emit("_requestRedraw"))}}onPinch(g){const A=this.getPointer(g.center);this.drag.pinched=!0,void 0===this.pinch.scale&&(this.pinch.scale=1);const t=this.pinch.scale*g.scale;this.zoom(t,A)}zoom(g,A){if(!0===this.options.zoomView){const t=this.body.view.scale;let C;g<1e-5&&(g=1e-5),g>10&&(g=10),void 0!==this.drag&&!0===this.drag.dragging&&(C=this.canvas.DOMtoCanvas(this.drag.pointer));const I=this.body.view.translation,e=g/t,i=(1-e)*A.x+I.x*e,o=(1-e)*A.y+I.y*e;if(this.body.view.scale=g,this.body.view.translation={x:i,y:o},null!=C){const g=this.canvas.canvasToDOM(C);this.drag.pointer.x=g.x,this.drag.pointer.y=g.y}this.body.emitter.emit("_requestRedraw"),tthis._checkShowPopup(A),this.options.tooltipDelay))),!0===this.options.hover&&this.selectionHandler.hoverObject(g,A)}_checkShowPopup(g){const A=this.canvas._XconvertDOMtoCanvas(g.x),t=this.canvas._YconvertDOMtoCanvas(g.y),C={left:A,top:t,right:A,bottom:t},I=void 0===this.popupObj?void 0:this.popupObj.id;let e=!1,i="node";if(void 0===this.popupObj){const g=this.body.nodeIndices,A=this.body.nodes;let t;const I=[];for(let i=0;i0&&(this.popupObj=A[I[I.length-1]],e=!0)}if(void 0===this.popupObj&&!1===e){const g=this.body.edgeIndices,A=this.body.edges;let t;const I=[];for(let e=0;e0&&(this.popupObj=A[I[I.length-1]],i="edge")}void 0!==this.popupObj?this.popupObj.id!==I&&(void 0===this.popup&&(this.popup=new Zp(this.canvas.frame)),this.popup.popupTargetType=i,this.popup.popupTargetId=this.popupObj.id,this.popup.setPosition(g.x+3,g.y-5),this.popup.setText(this.popupObj.getTitle()),this.popup.show(),this.body.emitter.emit("showPopup",this.popupObj.id)):void 0!==this.popup&&(this.popup.hide(),this.body.emitter.emit("hidePopup"))}_checkHidePopup(g){const A=this.selectionHandler._pointerToPositionObject(g);let t=!1;if("node"===this.popup.popupTargetType){if(void 0!==this.body.nodes[this.popup.popupTargetId]&&(t=this.body.nodes[this.popup.popupTargetId].isOverlappingWith(A),!0===t)){const A=this.selectionHandler.getNodeAt(g);t=void 0!==A&&A.id===this.popup.popupTargetId}}else void 0===this.selectionHandler.getNodeAt(g)&&void 0!==this.body.edges[this.popup.popupTargetId]&&(t=this.body.edges[this.popup.popupTargetId].isOverlappingWith(A));!1===t&&(this.popupObj=void 0,this.popup.hide(),this.body.emitter.emit("hidePopup"))}}class qT{getDistances(g,A,t){const C={},I=g.edges;for(let g=0;g2&&void 0!==arguments[2]&&arguments[2];const C=this.distanceSolver.getDistances(this.body,g,A);this._createL_matrix(C),this._createK_matrix(C),this._createE_matrix();let I=0;const e=Math.max(1e3,Math.min(10*this.body.nodeIndices.length,6e3));let i=1e9,o=0,n=0,s=0,r=0,a=0;for(;i>.01&&I1&&a<5;){a+=1,this._moveNode(o,n,s);var h=js(this._getEnergy(o),3);r=h[0],n=h[1],s=h[2]}}}_getHighestEnergyNode(g){const A=this.body.nodeIndices,t=this.body.nodes;let C=0,I=A[0],e=0,i=0;for(let o=0;o2&&void 0!==arguments[2]?arguments[2]:void 0;this.fake_use(g,A,t),this.abstract()}getTreeSize(g){return this.fake_use(g),this.abstract()}sort(g){this.fake_use(g),this.abstract()}fix(g,A){this.fake_use(g,A),this.abstract()}shift(g,A){this.fake_use(g,A),this.abstract()}}class AD extends gD{constructor(g){super(),this.layout=g}curveType(){return"horizontal"}getPosition(g){return g.x}setPosition(g,A){let t=arguments.length>2&&void 0!==arguments[2]?arguments[2]:void 0;void 0!==t&&this.layout.hierarchical.addToOrdering(g,t),g.x=A}getTreeSize(g){const A=this.layout.hierarchical.getTreeSize(this.layout.body.nodes,g);return{min:A.min_x,max:A.max_x}}sort(g){IT(g).call(g,function(g,A){return g.x-A.x})}fix(g,A){g.y=this.layout.options.hierarchical.levelSeparation*A,g.options.fixed.y=!0}shift(g,A){this.layout.body.nodes[g].x+=A}}class tD extends gD{constructor(g){super(),this.layout=g}curveType(){return"vertical"}getPosition(g){return g.y}setPosition(g,A){let t=arguments.length>2&&void 0!==arguments[2]?arguments[2]:void 0;void 0!==t&&this.layout.hierarchical.addToOrdering(g,t),g.y=A}getTreeSize(g){const A=this.layout.hierarchical.getTreeSize(this.layout.body.nodes,g);return{min:A.min_y,max:A.max_y}}sort(g){IT(g).call(g,function(g,A){return g.y-A.y})}fix(g,A){g.x=this.layout.options.hierarchical.levelSeparation*A,g.options.fixed.x=!0}shift(g,A){this.layout.body.nodes[g].y+=A}}var CD,ID,eD,iD,oD,nD,sD,rD,aD,dD={};function hD(){return eD?ID:(eD=1,function(){if(CD)return dD;CD=1;var g=st(),A=he().every;g({target:"Array",proto:!0,forced:!Ca()("every")},{every:function(g){return A(this,g,arguments.length>1?arguments[1]:void 0)}})}(),ID=gC()("Array","every"))}function lD(){if(oD)return iD;oD=1;var g=Ng(),A=hD(),t=Array.prototype;return iD=function(C){var I=C.every;return C===t||g(t,C)&&I===t.every?A:I},iD}function cD(){return sD?nD:(sD=1,nD=lD())}var uD=C(aD?rD:(aD=1,rD=cD()));function pD(g,A){const t=new uO;return pa(g).call(g,g=>{var A;pa(A=g.edges).call(A,g=>{g.connected&&t.add(g)})}),pa(t).call(t,g=>{const t=g.from.id,C=g.to.id;null==A[t]&&(A[t]=0),(null==A[C]||A[t]>=A[C])&&(A[C]=A[t]+1)}),A}function fD(g,A,t,C){var I;const e=hl(null),i=cw(I=[...Ay(C).call(C)]).call(I,(g,A)=>g+1+A.edges.length,0),o=t+"Id",n="to"===t?1:-1;for(const I of C){var s=js(I,2);const d=s[0],h=s[1];if(!C.has(d)||!g(h))continue;e[d]=0;const l=[h];let c,u=0;for(;c=l.pop();){var r,a;if(!C.has(d))continue;const g=e[c.id]+n;if(pa(r=eh(a=c.edges).call(a,g=>g.connected&&g.to!==g.from&&g[t]!==c&&C.has(g.toId)&&C.has(g.fromId))).call(r,C=>{const I=C[o],i=e[I];(null==i||A(g,i))&&(e[I]=g,l.push(C[t]))}),u>i)return pD(C,e);++u}}return e}class vD{constructor(){this.childrenReference={},this.parentReference={},this.trees={},this.distributionOrdering={},this.levels={},this.distributionIndex={},this.isTree=!1,this.treeIndex=-1}addRelation(g,A){void 0===this.childrenReference[g]&&(this.childrenReference[g]=[]),this.childrenReference[g].push(A),void 0===this.parentReference[A]&&(this.parentReference[A]=[]),this.parentReference[A].push(g)}checkIfTree(){for(const g in this.parentReference)if(this.parentReference[g].length>1)return void(this.isTree=!1);this.isTree=!0}numTrees(){return this.treeIndex+1}setTreeIndex(g,A){void 0!==A&&void 0===this.trees[g.id]&&(this.trees[g.id]=A,this.treeIndex=Math.max(A,this.treeIndex))}ensureLevel(g){void 0===this.levels[g]&&(this.levels[g]=0)}getMaxLevel(g){const A={},t=g=>{if(void 0!==A[g])return A[g];let C=this.levels[g];if(this.childrenReference[g]){const A=this.childrenReference[g];if(A.length>0)for(let g=0;gg-A);for(const C of t)g.set(C,A++);for(const A in this.levels)Object.prototype.hasOwnProperty.call(this.levels,A)&&(this.levels[A]=g.get(this.levels[A]))}getTreeSize(g,A){let t=1e9,C=-1e9,I=1e9,e=-1e9;for(const i in this.trees)if(Object.prototype.hasOwnProperty.call(this.trees,i)&&this.trees[i]===A){const A=g[i];t=Math.min(A.x,t),C=Math.max(A.x,C),I=Math.min(A.y,I),e=Math.max(A.y,e)}return{min_x:t,max_x:C,min_y:I,max_y:e}}hasSameParent(g,A){const t=this.parentReference[g.id],C=this.parentReference[A.id];if(void 0===t||void 0===C)return!1;for(let g=0;g{this.setupHierarchicalLayout()}),this.body.emitter.on("_dataLoaded",()=>{this.layoutNetwork()}),this.body.emitter.on("_resetHierarchicalLayout",()=>{this.setupHierarchicalLayout()}),this.body.emitter.on("_adjustEdgesForHierarchicalLayout",()=>{if(!0!==this.options.hierarchical.enabled)return;const g=this.direction.curveType();this.body.emitter.emit("_forceDisableDynamicCurves",g,!1)})}setOptions(g,A){if(void 0!==g){const t=this.options.hierarchical,C=t.enabled;if(op(["randomSeed","improvedLayout","clusterThreshold"],this.options,g),xp(this.options,g,"hierarchical"),void 0!==g.randomSeed&&this._resetRNG(g.randomSeed),!0===t.enabled)return!0===C&&this.body.emitter.emit("refresh",!0),"RL"===t.direction||"DU"===t.direction?t.levelSeparation>0&&(t.levelSeparation*=-1):t.levelSeparation<0&&(t.levelSeparation*=-1),this.setDirectionStrategy(),this.body.emitter.emit("_resetHierarchicalLayout"),this.adaptAllOptionsForHierarchicalLayout(A);if(!0===C)return this.body.emitter.emit("refresh"),sp(A,this.optionsBackup)}return A}_resetRNG(g){this.initialRandomSeed=g,this._rng=Xu(this.initialRandomSeed)}adaptAllOptionsForHierarchicalLayout(g){if(!0===this.options.hierarchical.enabled){const A=this.optionsBackup.physics;void 0===g.physics||!0===g.physics?(g.physics={enabled:void 0===A.enabled||A.enabled,solver:"hierarchicalRepulsion"},A.enabled=void 0===A.enabled||A.enabled,A.solver=A.solver||"barnesHut"):"object"==typeof g.physics?(A.enabled=void 0===g.physics.enabled||g.physics.enabled,A.solver=g.physics.solver||"barnesHut",g.physics.solver="hierarchicalRepulsion"):!1!==g.physics&&(A.solver="barnesHut",g.physics={solver:"hierarchicalRepulsion"});let t=this.direction.curveType();if(void 0===g.edges)this.optionsBackup.edges={smooth:{enabled:!0,type:"dynamic"}},g.edges={smooth:!1};else if(void 0===g.edges.smooth)this.optionsBackup.edges={smooth:{enabled:!0,type:"dynamic"}},g.edges.smooth=!1;else if("boolean"==typeof g.edges.smooth)this.optionsBackup.edges={smooth:g.edges.smooth},g.edges.smooth={enabled:g.edges.smooth,type:t};else{const A=g.edges.smooth;void 0!==A.type&&"dynamic"!==A.type&&(t=A.type),this.optionsBackup.edges={smooth:{enabled:void 0===A.enabled||A.enabled,type:void 0===A.type?"dynamic":A.type,roundness:void 0===A.roundness?.5:A.roundness,forceDirection:void 0!==A.forceDirection&&A.forceDirection}},g.edges.smooth={enabled:void 0===A.enabled||A.enabled,type:t,roundness:void 0===A.roundness?.5:A.roundness,forceDirection:void 0!==A.forceDirection&&A.forceDirection}}this.body.emitter.emit("_forceDisableDynamicCurves",t)}return g}positionInitially(g){if(!0!==this.options.hierarchical.enabled){this._resetRNG(this.initialRandomSeed);const A=g.length+50;for(let t=0;tC){const e=g.length;for(;g.length>C&&t<=A;){t+=1;const A=g.length;t%3==0?this.body.modules.clustering.clusterBridges(I):this.body.modules.clustering.clusterOutliers(I);if(A==g.length&&t%3!=0)return this._declusterAll(),this.body.emitter.emit("_layoutFailed"),void console.info("This network could not be positioned by this version of the improved layout algorithm. Please disable improvedLayout for better performance.")}this.body.modules.kamadaKawai.setOptions({springLength:Math.max(150,2*e)})}t>A&&console.info("The clustering didn't succeed within the amount of interations allowed, progressing with partial result."),this.body.modules.kamadaKawai.solve(g,this.body.edgeIndices,!0),this._shiftToCenter();const e=70;for(let A=0;A0){let g,A,t=!1,C=!1;for(A in this.lastNodeOnLevel={},this.hierarchical=new vD,this.body.nodes)Object.prototype.hasOwnProperty.call(this.body.nodes,A)&&(g=this.body.nodes[A],void 0!==g.options.level?(t=!0,this.hierarchical.levels[A]=g.options.level):C=!0);if(!0===C&&!0===t)throw new Error("To use the hierarchical layout, nodes require either no predefined levels or levels have to be defined for all nodes.");{if(!0===C){const g=this.options.hierarchical.sortMethod;"hubsize"===g?this._determineLevelsByHubsize():"directed"===g?this._determineLevelsDirected():"custom"===g&&this._determineLevelsCustomCallback()}for(const g in this.body.nodes)Object.prototype.hasOwnProperty.call(this.body.nodes,g)&&this.hierarchical.ensureLevel(g);const g=this._getDistribution();this._generateMap(),this._placeNodesByHierarchy(g),this._condenseHierarchy(),this._shiftToCenter()}}}_condenseHierarchy(){var g=this;let A=!1;const t={},C=(g,A)=>{const t=this.hierarchical.trees;for(const C in t)Object.prototype.hasOwnProperty.call(t,C)&&t[C]===g&&this.direction.shift(C,A)},I=()=>{const g=[];for(let A=0;A{if(!A[g.id]&&(A[g.id]=!0,this.hierarchical.childrenReference[g.id])){const t=this.hierarchical.childrenReference[g.id];if(t.length>0)for(let g=0;g1&&void 0!==arguments[1]?arguments[1]:1e9,C=1e9,I=1e9,e=1e9,i=-1e9;for(const o in A)if(Object.prototype.hasOwnProperty.call(A,o)){const n=g.body.nodes[o],s=g.hierarchical.levels[n.id],r=g.direction.getPosition(n),a=js(g._getSpaceAroundNode(n,A),2),d=a[0],h=a[1];C=Math.min(d,C),I=Math.min(h,I),s<=t&&(e=Math.min(r,e),i=Math.max(r,i))}return[e,i,C,I]},o=(g,A)=>{const t=this.hierarchical.getMaxLevel(g.id),C=this.hierarchical.getMaxLevel(A.id);return Math.min(t,C)},n=(g,A,t)=>{const C=this.hierarchical;for(let I=0;I1)for(let A=0;A2&&void 0!==arguments[2]&&arguments[2];const n=g.direction.getPosition(t),s=g.direction.getPosition(C),r=Math.abs(s-n),a=g.options.hierarchical.nodeSpacing;if(r>a){const n={},s={};e(t,n),e(C,s);const r=o(t,C),d=i(n,r),h=i(s,r),l=d[1],c=h[0],u=h[2];if(Math.abs(l-c)>a){let t=l-c+a;t<-u+a&&(t=-u+a),t<0&&(g._shiftBlock(C.id,t),A=!0,!0===I&&g._centerParent(C))}}},r=(g,C)=>{const I=C.id,o=C.edges,n=this.hierarchical.levels[C.id],s=this.options.hierarchical.levelSeparation*this.options.hierarchical.levelSeparation,r={},a=[];for(let g=0;g{let t=0;for(let C=0;C{let t=0;for(let C=0;C{let t=this.direction.getPosition(C);const I={};for(let C=0;C{const I=this.direction.getPosition(C);if(void 0===t[C.id]){const g={};e(C,g),t[C.id]=g}const o=i(t[C.id]),n=o[2],s=o[3],r=g-I;let a=0;r>0?a=Math.min(r,s-this.options.hierarchical.nodeSpacing):r<0&&(a=-Math.min(-r,n-this.options.hierarchical.nodeSpacing)),0!=a&&(this._shiftBlock(C.id,a),A=!0)})(c),c=l(g,o),(g=>{const t=this.direction.getPosition(C),I=js(this._getSpaceAroundNode(C),2),e=I[0],i=I[1],o=g-t;let n=t;o>0?n=Math.min(t+(i-this.options.hierarchical.nodeSpacing),g):o<0&&(n=Math.max(t-(e-this.options.hierarchical.nodeSpacing),g)),n!==t&&(this.direction.setPosition(C,n),A=!0)})(c)},a=g=>{let t=this.hierarchical.getLevels();t=t.toReversed();for(let C=0;C{let t=this.hierarchical.getLevels();t=t.toReversed();for(let C=0;C{for(const g in this.body.nodes)Object.prototype.hasOwnProperty.call(this.body.nodes,g)&&this._centerParent(this.body.nodes[g])},l=()=>{let g=this.hierarchical.getLevels();g=g.toReversed();for(let A=0;A{const g=I();let A=0;for(let t=0;t0&&Math.abs(o)0&&(i=this.direction.getPosition(C[g-1])+e),this.direction.setPosition(A,i,t),this._validatePositionAndContinue(A,t,i),I++}}}}_placeBranchNodes(g,A){var t;const C=this.hierarchical.childrenReference[g];if(void 0===C)return;const I=[];for(let g=0;gA&&void 0===this.positionedNodes[C.id]))return;{const A=this.options.hierarchical.nodeSpacing;let i;i=0===t?this.direction.getPosition(this.body.nodes[g]):this.direction.getPosition(I[t-1])+A,this.direction.setPosition(C,i,e),this._validatePositionAndContinue(C,e,i)}}const e=this._getCenterPosition(I);this.direction.setPosition(this.body.nodes[g],e,A)}_validatePositionAndContinue(g,A,t){if(this.hierarchical.isTree){if(void 0!==this.lastNodeOnLevel[A]){const C=this.direction.getPosition(this.body.nodes[this.lastNodeOnLevel[A]]);if(t-C{var t;-1!==Jh(t=this.body.edgeIndices).call(t,g.id)&&A.push(g)}),A}_getHubSizes(){const g={};hp(this.body.nodeIndices,A=>{const t=this.body.nodes[A],C=this._getActiveEdges(t).length;g[C]=!0});const A=[];return hp(g,g=>{A.push(Number(g))}),IT(A).call(A,function(g,A){return A-g}),A}_determineLevelsByHubsize(){const g=(g,A)=>{this.hierarchical.levelDownstream(g,A)},A=this._getHubSizes();for(let t=0;t{const t=this.body.nodes[A];C===this._getActiveEdges(t).length&&this._crawlNetwork(g,A)})}}_determineLevelsCustomCallback(){this._crawlNetwork((g,A,t)=>{let C=this.hierarchical.levels[g.id];void 0===C&&(C=this.hierarchical.levels[g.id]=1e5);const I=(Ff.cloneOptions(g,"node"),Ff.cloneOptions(A,"node"),void Ff.cloneOptions(t,"edge"));this.hierarchical.levels[A.id]=C+I}),this.hierarchical.setMinLevelToZero()}_determineLevelsDirected(){var g;const A=cw(g=this.body.nodeIndices).call(g,(g,A)=>(g.set(A,this.body.nodes[A]),g),new Zx);"roots"===this.options.hierarchical.shakeTowards?this.hierarchical.levels=function(g){return fD(A=>{var t,C;return uD(t=eh(C=A.edges).call(C,A=>g.has(A.toId))).call(t,g=>g.from===A)},(g,A)=>A{var t,C;return uD(t=eh(C=A.edges).call(C,A=>g.has(A.toId))).call(t,g=>g.to===A)},(g,A)=>A>g,"from",g)}(A),this.hierarchical.setMinLevelToZero()}_generateMap(){this._crawlNetwork((g,A)=>{this.hierarchical.levels[A.id]>this.hierarchical.levels[g.id]&&this.hierarchical.addRelation(g.id,A.id)}),this.hierarchical.checkIfTree()}_crawlNetwork(){let g=arguments.length>0&&void 0!==arguments[0]?arguments[0]:function(){},A=arguments.length>1?arguments[1]:void 0;const t={},C=(A,I)=>{if(void 0===t[A.id]){let e;this.hierarchical.setTreeIndex(A,I),t[A.id]=!0;const i=this._getActiveEdges(A);for(let t=0;t{if(t[g])return;t[g]=!0,this.direction.shift(g,A);const I=this.hierarchical.childrenReference[g];if(void 0!==I)for(let g=0;g{const t=this.hierarchical.parentReference[A];if(void 0!==t)for(let A=0;A{const t=this.hierarchical.parentReference[A];if(void 0!==t)for(let C=0;C{this._clean()}),this.body.emitter.on("_dataChanged",IC(I=this._restore).call(I,this)),this.body.emitter.on("_resetData",IC(e=this._restore).call(e,this))}_restore(){!1!==this.inMode&&(!0===this.options.initiallyActive?this.enableEditMode():this.disableEditMode())}setOptions(g,A,t){void 0!==A&&(void 0!==A.locale?this.options.locale=A.locale:this.options.locale=t.locale,void 0!==A.locales?this.options.locales=A.locales:this.options.locales=t.locales),void 0!==g&&("boolean"==typeof g?this.options.enabled=g:(this.options.enabled=!0,sp(this.options,g)),!0===this.options.initiallyActive&&(this.editMode=!0),this._setup())}toggleEditMode(){!0===this.editMode?this.disableEditMode():this.enableEditMode()}enableEditMode(){this.editMode=!0,this._clean(),!0===this.guiEnabled&&(this.manipulationDiv.style.display="block",this.closeDiv.style.display="block",this.editModeDiv.style.display="none",this.showManipulatorToolbar())}disableEditMode(){this.editMode=!1,this._clean(),!0===this.guiEnabled&&(this.manipulationDiv.style.display="none",this.closeDiv.style.display="none",this.editModeDiv.style.display="block",this._createEditButton())}showManipulatorToolbar(){if(this._clean(),this.manipulationDOM={},!0===this.guiEnabled){var g,A;this.editMode=!0,this.manipulationDiv.style.display="block",this.closeDiv.style.display="block";const t=this.selectionHandler.getSelectedNodeCount(),C=this.selectionHandler.getSelectedEdgeCount(),I=t+C,e=this.options.locales[this.options.locale];let i=!1;!1!==this.options.addNode&&(this._createAddNodeButton(e),i=!0),!1!==this.options.addEdge&&(!0===i?this._createSeperator(1):i=!0,this._createAddEdgeButton(e)),1===t&&"function"==typeof this.options.editNode?(!0===i?this._createSeperator(2):i=!0,this._createEditNodeButton(e)):1===C&&0===t&&!1!==this.options.editEdge&&(!0===i?this._createSeperator(3):i=!0,this._createEditEdgeButton(e)),0!==I&&(t>0&&!1!==this.options.deleteNode||0===t&&!1!==this.options.deleteEdge)&&(!0===i&&this._createSeperator(4),this._createDeleteButton(e)),this._bindElementEvents(this.closeDiv,IC(g=this.toggleEditMode).call(g,this)),this._temporaryBindEvent("select",IC(A=this.showManipulatorToolbar).call(A,this))}this.body.emitter.emit("_redraw")}addNodeMode(){var g;if(!0!==this.editMode&&this.enableEditMode(),this._clean(),this.inMode="addNode",!0===this.guiEnabled){var A;const g=this.options.locales[this.options.locale];this.manipulationDOM={},this._createBackButton(g),this._createSeperator(),this._createDescription(g.addDescription||this.options.locales.en.addDescription),this._bindElementEvents(this.closeDiv,IC(A=this.toggleEditMode).call(A,this))}this._temporaryBindEvent("click",IC(g=this._performAddNode).call(g,this))}editNode(){!0!==this.editMode&&this.enableEditMode(),this._clean();const g=this.selectionHandler.getSelectedNodes()[0];if(void 0!==g){if(this.inMode="editNode","function"!=typeof this.options.editNode)throw new Error("No function has been configured to handle the editing of nodes.");if(!0!==g.isCluster){const A=sp({},g.options,!1);if(A.x=g.x,A.y=g.y,2!==this.options.editNode.length)throw new Error("The function for edit does not support two arguments (data, callback)");this.options.editNode(A,g=>{null!=g&&"editNode"===this.inMode&&this.body.data.nodes.getDataSet().update(g),this.showManipulatorToolbar()})}else alert(this.options.locales[this.options.locale].editClusterError||this.options.locales.en.editClusterError)}else this.showManipulatorToolbar()}addEdgeMode(){var g,A,t,C,I;if(!0!==this.editMode&&this.enableEditMode(),this._clean(),this.inMode="addEdge",!0===this.guiEnabled){var e;const g=this.options.locales[this.options.locale];this.manipulationDOM={},this._createBackButton(g),this._createSeperator(),this._createDescription(g.edgeDescription||this.options.locales.en.edgeDescription),this._bindElementEvents(this.closeDiv,IC(e=this.toggleEditMode).call(e,this))}this._temporaryBindUI("onTouch",IC(g=this._handleConnect).call(g,this)),this._temporaryBindUI("onDragEnd",IC(A=this._finishConnect).call(A,this)),this._temporaryBindUI("onDrag",IC(t=this._dragControlNode).call(t,this)),this._temporaryBindUI("onRelease",IC(C=this._finishConnect).call(C,this)),this._temporaryBindUI("onDragStart",IC(I=this._dragStartEdge).call(I,this)),this._temporaryBindUI("onHold",()=>{})}editEdgeMode(){if(!0!==this.editMode&&this.enableEditMode(),this._clean(),this.inMode="editEdge","object"==typeof this.options.editEdge&&"function"==typeof this.options.editEdge.editWithoutDrag&&(this.edgeBeingEditedId=this.selectionHandler.getSelectedEdgeIds()[0],void 0!==this.edgeBeingEditedId)){const g=this.body.edges[this.edgeBeingEditedId];return void this._performEditEdge(g.from.id,g.to.id)}if(!0===this.guiEnabled){var g;const A=this.options.locales[this.options.locale];this.manipulationDOM={},this._createBackButton(A),this._createSeperator(),this._createDescription(A.editEdgeDescription||this.options.locales.en.editEdgeDescription),this._bindElementEvents(this.closeDiv,IC(g=this.toggleEditMode).call(g,this))}if(this.edgeBeingEditedId=this.selectionHandler.getSelectedEdgeIds()[0],void 0!==this.edgeBeingEditedId){var A,t,C,I;const g=this.body.edges[this.edgeBeingEditedId],e=this._getNewTargetNode(g.from.x,g.from.y),i=this._getNewTargetNode(g.to.x,g.to.y);this.temporaryIds.nodes.push(e.id),this.temporaryIds.nodes.push(i.id),this.body.nodes[e.id]=e,this.body.nodeIndices.push(e.id),this.body.nodes[i.id]=i,this.body.nodeIndices.push(i.id),this._temporaryBindUI("onTouch",IC(A=this._controlNodeTouch).call(A,this)),this._temporaryBindUI("onTap",()=>{}),this._temporaryBindUI("onHold",()=>{}),this._temporaryBindUI("onDragStart",IC(t=this._controlNodeDragStart).call(t,this)),this._temporaryBindUI("onDrag",IC(C=this._controlNodeDrag).call(C,this)),this._temporaryBindUI("onDragEnd",IC(I=this._controlNodeDragEnd).call(I,this)),this._temporaryBindUI("onMouseMove",()=>{}),this._temporaryBindEvent("beforeDrawing",A=>{const t=g.edgeType.findBorderPositions(A);!1===e.selected&&(e.x=t.from.x,e.y=t.from.y),!1===i.selected&&(i.x=t.to.x,i.y=t.to.y)}),this.body.emitter.emit("_redraw")}else this.showManipulatorToolbar()}deleteSelected(){!0!==this.editMode&&this.enableEditMode(),this._clean(),this.inMode="delete";const g=this.selectionHandler.getSelectedNodeIds(),A=this.selectionHandler.getSelectedEdgeIds();let t;if(g.length>0){for(let A=0;A0&&"function"==typeof this.options.deleteEdge&&(t=this.options.deleteEdge);if("function"==typeof t){const C={nodes:g,edges:A};if(2!==t.length)throw new Error("The function for delete does not support two arguments (data, callback)");t(C,g=>{null!=g&&"delete"===this.inMode?(this.body.data.edges.getDataSet().remove(g.edges),this.body.data.nodes.getDataSet().remove(g.nodes),this.body.emitter.emit("startSimulation"),this.showManipulatorToolbar()):(this.body.emitter.emit("startSimulation"),this.showManipulatorToolbar())})}else this.body.data.edges.getDataSet().remove(A),this.body.data.nodes.getDataSet().remove(g),this.body.emitter.emit("startSimulation"),this.showManipulatorToolbar()}_setup(){!0===this.options.enabled?(this.guiEnabled=!0,this._createWrappers(),!1===this.editMode?this._createEditButton():this.showManipulatorToolbar()):(this._removeManipulationDOM(),this.guiEnabled=!1)}_createWrappers(){var g,A;(void 0===this.manipulationDiv&&(this.manipulationDiv=document.createElement("div"),this.manipulationDiv.className="vis-manipulation",!0===this.editMode?this.manipulationDiv.style.display="block":this.manipulationDiv.style.display="none",this.canvas.frame.appendChild(this.manipulationDiv)),void 0===this.editModeDiv&&(this.editModeDiv=document.createElement("div"),this.editModeDiv.className="vis-edit-mode",!0===this.editMode?this.editModeDiv.style.display="none":this.editModeDiv.style.display="block",this.canvas.frame.appendChild(this.editModeDiv)),void 0===this.closeDiv)&&(this.closeDiv=document.createElement("button"),this.closeDiv.className="vis-close",this.closeDiv.setAttribute("aria-label",null!==(g=null===(A=this.options.locales[this.options.locale])||void 0===A?void 0:A.close)&&void 0!==g?g:this.options.locales.en.close),this.closeDiv.style.display=this.manipulationDiv.style.display,this.canvas.frame.appendChild(this.closeDiv))}_getNewTargetNode(g,A){const t=sp({},this.options.controlNodeStyle);t.id="targetNode"+Sf(),t.hidden=!1,t.physics=!1,t.x=g,t.y=A;const C=this.body.functions.createNode(t);return C.shape.boundingBox={left:g,right:g,top:A,bottom:A},C}_createEditButton(){var g;this._clean(),this.manipulationDOM={},tp(this.editModeDiv);const A=this.options.locales[this.options.locale],t=this._createButton("editMode","vis-edit vis-edit-mode",A.edit||this.options.locales.en.edit);this.editModeDiv.appendChild(t),this._bindElementEvents(t,IC(g=this.toggleEditMode).call(g,this))}_clean(){this.inMode=!1,!0===this.guiEnabled&&(tp(this.editModeDiv),tp(this.manipulationDiv),this._cleanupDOMEventListeners()),this._cleanupTemporaryNodesAndEdges(),this._unbindTemporaryUIs(),this._unbindTemporaryEvents(),this.body.emitter.emit("restorePhysics")}_cleanupDOMEventListeners(){for(const A of Va(g=this._domEventListenerCleanupQueue).call(g,0)){var g;A()}}_removeManipulationDOM(){this._clean(),tp(this.manipulationDiv),tp(this.editModeDiv),tp(this.closeDiv),this.manipulationDiv&&this.canvas.frame.removeChild(this.manipulationDiv),this.editModeDiv&&this.canvas.frame.removeChild(this.editModeDiv),this.closeDiv&&this.canvas.frame.removeChild(this.closeDiv),this.manipulationDiv=void 0,this.editModeDiv=void 0,this.closeDiv=void 0}_createSeperator(){let g=arguments.length>0&&void 0!==arguments[0]?arguments[0]:1;this.manipulationDOM["seperatorLineDiv"+g]=document.createElement("div"),this.manipulationDOM["seperatorLineDiv"+g].className="vis-separator-line",this.manipulationDiv.appendChild(this.manipulationDOM["seperatorLineDiv"+g])}_createAddNodeButton(g){var A;const t=this._createButton("addNode","vis-add",g.addNode||this.options.locales.en.addNode);this.manipulationDiv.appendChild(t),this._bindElementEvents(t,IC(A=this.addNodeMode).call(A,this))}_createAddEdgeButton(g){var A;const t=this._createButton("addEdge","vis-connect",g.addEdge||this.options.locales.en.addEdge);this.manipulationDiv.appendChild(t),this._bindElementEvents(t,IC(A=this.addEdgeMode).call(A,this))}_createEditNodeButton(g){var A;const t=this._createButton("editNode","vis-edit",g.editNode||this.options.locales.en.editNode);this.manipulationDiv.appendChild(t),this._bindElementEvents(t,IC(A=this.editNode).call(A,this))}_createEditEdgeButton(g){var A;const t=this._createButton("editEdge","vis-edit",g.editEdge||this.options.locales.en.editEdge);this.manipulationDiv.appendChild(t),this._bindElementEvents(t,IC(A=this.editEdgeMode).call(A,this))}_createDeleteButton(g){var A;let t;t=this.options.rtl?"vis-delete-rtl":"vis-delete";const C=this._createButton("delete",t,g.del||this.options.locales.en.del);this.manipulationDiv.appendChild(C),this._bindElementEvents(C,IC(A=this.deleteSelected).call(A,this))}_createBackButton(g){var A;const t=this._createButton("back","vis-back",g.back||this.options.locales.en.back);this.manipulationDiv.appendChild(t),this._bindElementEvents(t,IC(A=this.showManipulatorToolbar).call(A,this))}_createButton(g,A,t){let C=arguments.length>3&&void 0!==arguments[3]?arguments[3]:"vis-label";return this.manipulationDOM[g+"Div"]=document.createElement("button"),this.manipulationDOM[g+"Div"].className="vis-button "+A,this.manipulationDOM[g+"Label"]=document.createElement("div"),this.manipulationDOM[g+"Label"].className=C,this.manipulationDOM[g+"Label"].innerText=t,this.manipulationDOM[g+"Div"].appendChild(this.manipulationDOM[g+"Label"]),this.manipulationDOM[g+"Div"]}_createDescription(g){this.manipulationDOM.descriptionLabel=document.createElement("div"),this.manipulationDOM.descriptionLabel.className="vis-none",this.manipulationDOM.descriptionLabel.innerText=g,this.manipulationDiv.appendChild(this.manipulationDOM.descriptionLabel)}_temporaryBindEvent(g,A){this.temporaryEventFunctions.push({event:g,boundFunction:A}),this.body.emitter.on(g,A)}_temporaryBindUI(g,A){if(void 0===this.body.eventListeners[g])throw new Error("This UI function does not exist. Typo? You tried: "+g+" possible are: "+Tl(Fr(this.body.eventListeners)));this.temporaryUIFunctions[g]=this.body.eventListeners[g],this.body.eventListeners[g]=A}_unbindTemporaryUIs(){for(const g in this.temporaryUIFunctions)Object.prototype.hasOwnProperty.call(this.temporaryUIFunctions,g)&&(this.body.eventListeners[g]=this.temporaryUIFunctions[g],delete this.temporaryUIFunctions[g]);this.temporaryUIFunctions={}}_unbindTemporaryEvents(){for(let g=0;g{t.destroy()});const C=g=>{let t=g.keyCode,C=g.key;"Enter"!==C&&" "!==C&&13!==t&&32!==t||A()};g.addEventListener("keyup",C,!1),this._domEventListenerCleanupQueue.push(()=>{g.removeEventListener("keyup",C,!1)})}_cleanupTemporaryNodesAndEdges(){for(let t=0;t=0;g--)if(I[g]!==this.selectedControlNode.id){e=this.body.nodes[I[g]];break}if(void 0!==e&&void 0!==this.selectedControlNode)if(!0===e.isCluster)alert(this.options.locales[this.options.locale].createEdgeError||this.options.locales.en.createEdgeError);else{const g=this.body.nodes[this.temporaryIds.nodes[0]];this.selectedControlNode.id===g.id?this._performEditEdge(e.id,C.to.id):this._performEditEdge(C.from.id,e.id)}else C.updateEdgeType(),this.body.emitter.emit("restorePhysics");this.body.emitter.emit("_redraw")}_handleConnect(g){if((new Date).valueOf()-this.touchTime>100){this.lastTouch=this.body.functions.getPointer(g.center),this.lastTouch.translation=_t({},this.body.view.translation),this.interactionHandler.drag.pointer=this.lastTouch,this.interactionHandler.drag.translation=this.lastTouch.translation;const A=this.lastTouch,t=this.selectionHandler.getNodeAt(A);if(void 0!==t)if(!0===t.isCluster)alert(this.options.locales[this.options.locale].createEdgeError||this.options.locales.en.createEdgeError);else{const g=this._getNewTargetNode(t.x,t.y);this.body.nodes[g.id]=g,this.body.nodeIndices.push(g.id);const A=this.body.functions.createEdge({id:"connectionEdge"+Sf(),from:t.id,to:g.id,physics:!1,smooth:{enabled:!0,type:"continuous",roundness:.5}});this.body.edges[A.id]=A,this.body.edgeIndices.push(A.id),this.temporaryIds.nodes.push(g.id),this.temporaryIds.edges.push(A.id)}this.touchTime=(new Date).valueOf()}}_dragControlNode(g){const A=this.body.functions.getPointer(g.center),t=this.selectionHandler._pointerToPositionObject(A);let C;void 0!==this.temporaryIds.edges[0]&&(C=this.body.edges[this.temporaryIds.edges[0]].fromId);const I=this.selectionHandler._getAllNodesOverlappingWith(t);let e;for(let g=I.length-1;g>=0;g--){var i;if(-1===Jh(i=this.temporaryIds.nodes).call(i,I[g])){e=this.body.nodes[I[g]];break}}if(g.controlEdge={from:C,to:e?e.id:void 0},this.selectionHandler.generateClickEvent("controlNodeDragging",g,A),void 0!==this.temporaryIds.nodes[0]){const g=this.body.nodes[this.temporaryIds.nodes[0]];g.x=this.canvas._XconvertDOMtoCanvas(A.x),g.y=this.canvas._YconvertDOMtoCanvas(A.y),this.body.emitter.emit("_redraw")}else this.interactionHandler.onDrag(g)}_finishConnect(g){const A=this.body.functions.getPointer(g.center),t=this.selectionHandler._pointerToPositionObject(A);let C;void 0!==this.temporaryIds.edges[0]&&(C=this.body.edges[this.temporaryIds.edges[0]].fromId);const I=this.selectionHandler._getAllNodesOverlappingWith(t);let e;for(let g=I.length-1;g>=0;g--){var i;if(-1===Jh(i=this.temporaryIds.nodes).call(i,I[g])){e=this.body.nodes[I[g]];break}}this._cleanupTemporaryNodesAndEdges(),void 0!==e&&(!0===e.isCluster?alert(this.options.locales[this.options.locale].createEdgeError||this.options.locales.en.createEdgeError):void 0!==this.body.nodes[C]&&void 0!==this.body.nodes[e.id]&&this._performAddEdge(C,e.id)),g.controlEdge={from:C,to:e?e.id:void 0},this.selectionHandler.generateClickEvent("controlNodeDragEnd",g,A),this.body.emitter.emit("_redraw")}_dragStartEdge(g){const A=this.lastTouch;this.selectionHandler.generateClickEvent("dragStart",g,A,void 0,!0)}_performAddNode(g){const A={id:Sf(),x:g.pointer.canvas.x,y:g.pointer.canvas.y,label:"new"};if("function"==typeof this.options.addNode){if(2!==this.options.addNode.length)throw this.showManipulatorToolbar(),new Error("The function for add does not support two arguments (data,callback)");this.options.addNode(A,g=>{null!=g&&"addNode"===this.inMode&&this.body.data.nodes.getDataSet().add(g),this.showManipulatorToolbar()})}else this.body.data.nodes.getDataSet().add(A),this.showManipulatorToolbar()}_performAddEdge(g,A){const t={from:g,to:A};if("function"==typeof this.options.addEdge){if(2!==this.options.addEdge.length)throw new Error("The function for connect does not support two arguments (data,callback)");this.options.addEdge(t,g=>{null!=g&&"addEdge"===this.inMode&&(this.body.data.edges.getDataSet().add(g),this.selectionHandler.unselectAll(),this.showManipulatorToolbar())})}else this.body.data.edges.getDataSet().add(t),this.selectionHandler.unselectAll(),this.showManipulatorToolbar()}_performEditEdge(g,A){const t={id:this.edgeBeingEditedId,from:g,to:A,label:this.body.data.edges.get(this.edgeBeingEditedId).label};let C=this.options.editEdge;if("object"==typeof C&&(C=C.editWithoutDrag),"function"==typeof C){if(2!==C.length)throw new Error("The function for edit does not support two arguments (data, callback)");C(t,g=>{null==g||"editEdge"!==this.inMode?(this.body.edges[t.id].updateEdgeType(),this.body.emitter.emit("_redraw"),this.showManipulatorToolbar()):(this.body.data.edges.getDataSet().update(g),this.selectionHandler.unselectAll(),this.showManipulatorToolbar())})}else this.body.data.edges.getDataSet().update(t),this.selectionHandler.unselectAll(),this.showManipulatorToolbar()}}var yD,wD,xD,ED,OD,TD,DD;function ND(){return xD?wD:(xD=1,yD||(yD=1,st()({target:"Number",stat:!0},{isNaN:function(g){return g!=g}})),wD=Tg().Number.isNaN)}function kD(){return OD?ED:(OD=1,ED=ND())}var RD=C(DD?TD:(DD=1,TD=kD()));class PD{constructor(g,A,t,C){var I;if(this.body=g,this.images=A,this.groups=t,this.layoutEngine=C,this.body.functions.createNode=IC(I=this.create).call(I,this),this.nodesListeners={add:(g,A)=>{this.add(A.items)},update:(g,A)=>{this.update(A.items,A.data,A.oldData)},remove:(g,A)=>{this.remove(A.items)}},this.defaultOptions={borderWidth:1,borderWidthSelected:void 0,brokenImage:void 0,color:{border:"#2B7CE9",background:"#97C2FC",highlight:{border:"#2B7CE9",background:"#D2E5FF"},hover:{border:"#2B7CE9",background:"#D2E5FF"}},opacity:void 0,fixed:{x:!1,y:!1},font:{color:"#343434",size:14,face:"arial",background:"none",strokeWidth:0,strokeColor:"#ffffff",align:"center",vadjust:0,multi:!1,bold:{mod:"bold"},boldital:{mod:"bold italic"},ital:{mod:"italic"},mono:{mod:"",size:15,face:"monospace",vadjust:2}},group:void 0,hidden:!1,icon:{face:"FontAwesome",code:void 0,size:50,color:"#2B7CE9"},image:void 0,imagePadding:{top:0,right:0,bottom:0,left:0},label:void 0,labelHighlightBold:!0,level:void 0,margin:{top:5,right:5,bottom:5,left:5},mass:1,physics:!0,scaling:{min:10,max:30,label:{enabled:!1,min:14,max:30,maxVisible:30,drawThreshold:5},customScalingFunction:function(g,A,t,C){if(A===g)return.5;{const t=1/(A-g);return Math.max(0,(C-g)*t)}}},shadow:{enabled:!1,color:"rgba(0,0,0,0.5)",size:10,x:5,y:5},shape:"ellipse",shapeProperties:{borderDashes:!1,borderRadius:6,interpolation:!0,useImageSize:!1,useBorderWithImage:!1,coordinateOrigin:"center"},size:25,title:void 0,value:void 0,x:void 0,y:void 0},this.defaultOptions.mass<=0)throw"Internal error: mass in defaultOptions of NodesHandler may not be zero or negative";this.options=wp(this.defaultOptions),this.bindEventListeners()}bindEventListeners(){var g,A;this.body.emitter.on("refreshNodes",IC(g=this.refresh).call(g,this)),this.body.emitter.on("refresh",IC(A=this.refresh).call(A,this)),this.body.emitter.on("destroy",()=>{hp(this.nodesListeners,(g,A)=>{this.body.data.nodes&&this.body.data.nodes.off(A,g)}),delete this.body.functions.createNode,delete this.nodesListeners.add,delete this.nodesListeners.update,delete this.nodesListeners.remove,delete this.nodesListeners})}setOptions(g){if(void 0!==g){if(Wy.parseOptions(this.options,g),void 0!==g.opacity&&(RD(g.opacity)||!mv(g.opacity)||g.opacity<0||g.opacity>1?console.error("Invalid option for node opacity. Value must be between 0 and 1, found: "+g.opacity):this.options.opacity=g.opacity),void 0!==g.shape)for(const g in this.body.nodes)Object.prototype.hasOwnProperty.call(this.body.nodes,g)&&this.body.nodes[g].updateShape();if(void 0!==g.font||void 0!==g.widthConstraint||void 0!==g.heightConstraint)for(const g of Fr(this.body.nodes))this.body.nodes[g].updateLabelModule(),this.body.nodes[g].needsRefresh();if(void 0!==g.size)for(const g in this.body.nodes)Object.prototype.hasOwnProperty.call(this.body.nodes,g)&&this.body.nodes[g].needsRefresh();void 0===g.hidden&&void 0===g.physics||this.body.emitter.emit("_dataChanged")}}setData(g){let A=arguments.length>1&&void 0!==arguments[1]&&arguments[1];const t=this.body.data.nodes;if(QT("id",g))this.body.data.nodes=g;else if(cr(g))this.body.data.nodes=new VT,this.body.data.nodes.add(g);else{if(g)throw new TypeError("Array or DataSet expected");this.body.data.nodes=new VT}if(t&&hp(this.nodesListeners,function(g,A){t.off(A,g)}),this.body.nodes={},this.body.data.nodes){const g=this;hp(this.nodesListeners,function(A,t){g.body.data.nodes.on(t,A)});const A=this.body.data.nodes.getIds();this.add(A,!0)}!1===A&&this.body.emitter.emit("_dataChanged")}add(g){let A,t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];const C=[];for(let t=0;t1&&void 0!==arguments[1]?arguments[1]:Wy)(g,this.body,this.images,this.groups,this.options,this.defaultOptions)}refresh(){let g=arguments.length>0&&void 0!==arguments[0]&&arguments[0];hp(this.body.nodes,(A,t)=>{const C=this.body.data.nodes.get(t);void 0!==C&&(!0===g&&A.setOptions({x:null,y:null}),A.setOptions({fixed:!1}),A.setOptions(C))})}getPositions(g){const A={};if(void 0!==g){if(!0===cr(g)){for(let t=0;t{this.body.emitter.emit("startSimulation")},0)):console.error("Node id supplied to moveNode does not exist. Provided: ",g)}}class MD{constructor(g,A,t){this.body=g,this.physicsBody=A,this.setOptions(t),this._rng=Xu("BARNES HUT SOLVER")}setOptions(g){this.options=g,this.thetaInversed=1/this.options.theta,this.overlapAvoidanceFactor=1-Math.max(0,Math.min(1,this.options.avoidOverlap))}solve(){if(0!==this.options.gravitationalConstant&&this.physicsBody.physicsNodeIndices.length>0){let g;const A=this.body.nodes,t=this.physicsBody.physicsNodeIndices,C=t.length,I=this._formBarnesHutTree(A,t);this.barnesHutTree=I;for(let e=0;e0&&this._getForceContributions(I.root,g)}}_getForceContributions(g,A){this._getForceContribution(g.children.NW,A),this._getForceContribution(g.children.NE,A),this._getForceContribution(g.children.SW,A),this._getForceContribution(g.children.SE,A)}_getForceContribution(g,A){if(g.childrenCount>0){const t=g.centerOfMass.x-A.x,C=g.centerOfMass.y-A.y,I=Math.sqrt(t*t+C*C);I*g.calcSize>this.thetaInversed?this._calculateForces(I,t,C,A,g):4===g.childrenCount?this._getForceContributions(g,A):g.children.data.id!=A.id&&this._calculateForces(I,t,C,A,g)}}_calculateForces(g,A,t,C,I){0===g&&(A=g=.1),this.overlapAvoidanceFactor<1&&C.shape.radius&&(g=Math.max(.1+this.overlapAvoidanceFactor*C.shape.radius,g-C.shape.radius));const e=this.options.gravitationalConstant*I.mass*C.options.mass/Math.pow(g,3),i=A*e,o=t*e;this.physicsBody.forces[C.id].x+=i,this.physicsBody.forces[C.id].y+=o}_formBarnesHutTree(g,A){let t;const C=A.length;let I=g[A[0]].x,e=g[A[0]].y,i=g[A[0]].x,o=g[A[0]].y;for(let t=1;t0&&(ni&&(i=n),so&&(o=s))}const n=Math.abs(i-I)-Math.abs(o-e);n>0?(e-=.5*n,o+=.5*n):(I+=.5*n,i-=.5*n);const s=Math.max(1e-5,Math.abs(i-I)),r=.5*s,a=.5*(I+i),d=.5*(e+o),h={root:{centerOfMass:{x:0,y:0},mass:0,range:{minX:a-r,maxX:a+r,minY:d-r,maxY:d+r},size:s,calcSize:1/s,children:{data:null},maxWidth:0,level:0,childrenCount:4}};this._splitBranch(h.root);for(let I=0;I0&&this._placeInTree(h.root,t);return h}_updateBranchMass(g,A){const t=g.centerOfMass,C=g.mass+A.options.mass,I=1/C;t.x=t.x*g.mass+A.x*A.options.mass,t.x*=I,t.y=t.y*g.mass+A.y*A.options.mass,t.y*=I,g.mass=C;const e=Math.max(Math.max(A.height,A.radius),A.width);g.maxWidth=g.maxWidthA.x?C.maxY>A.y?"NW":"SW":C.maxY>A.y?"NE":"SE",this._placeInRegion(g,A,I)}_placeInRegion(g,A,t){const C=g.children[t];switch(C.childrenCount){case 0:C.children.data=A,C.childrenCount=1,this._updateBranchMass(C,A);break;case 1:C.children.data.x===A.x&&C.children.data.y===A.y?(A.x+=this._rng(),A.y+=this._rng()):(this._splitBranch(C),this._placeInTree(C,A));break;case 4:this._placeInTree(C,A)}}_splitBranch(g){let A=null;1===g.childrenCount&&(A=g.children.data,g.mass=0,g.centerOfMass.x=0,g.centerOfMass.y=0),g.childrenCount=4,g.children.data=null,this._insertRegion(g,"NW"),this._insertRegion(g,"NE"),this._insertRegion(g,"SW"),this._insertRegion(g,"SE"),null!=A&&this._placeInTree(g,A)}_insertRegion(g,A){let t,C,I,e;const i=.5*g.size;switch(A){case"NW":t=g.range.minX,C=g.range.minX+i,I=g.range.minY,e=g.range.minY+i;break;case"NE":t=g.range.minX+i,C=g.range.maxX,I=g.range.minY,e=g.range.minY+i;break;case"SW":t=g.range.minX,C=g.range.minX+i,I=g.range.minY+i,e=g.range.maxY;break;case"SE":t=g.range.minX+i,C=g.range.maxX,I=g.range.minY+i,e=g.range.maxY}g.children[A]={centerOfMass:{x:0,y:0},mass:0,range:{minX:t,maxX:C,minY:I,maxY:e},size:.5*g.size,calcSize:2*g.calcSize,children:{data:null},maxWidth:0,level:g.level+1,childrenCount:0}}_debug(g,A){void 0!==this.barnesHutTree&&(g.lineWidth=1,this._drawBranch(this.barnesHutTree.root,g,A))}_drawBranch(g,A,t){void 0===t&&(t="#FF0000"),4===g.childrenCount&&(this._drawBranch(g.children.NW,A),this._drawBranch(g.children.NE,A),this._drawBranch(g.children.SE,A),this._drawBranch(g.children.SW,A)),A.strokeStyle=t,A.beginPath(),A.moveTo(g.range.minX,g.range.minY),A.lineTo(g.range.maxX,g.range.minY),A.stroke(),A.beginPath(),A.moveTo(g.range.maxX,g.range.minY),A.lineTo(g.range.maxX,g.range.maxY),A.stroke(),A.beginPath(),A.moveTo(g.range.maxX,g.range.maxY),A.lineTo(g.range.minX,g.range.maxY),A.stroke(),A.beginPath(),A.moveTo(g.range.minX,g.range.maxY),A.lineTo(g.range.minX,g.range.minY),A.stroke()}}class zD{constructor(g,A,t){this.body=g,this.physicsBody=A,this.setOptions(t)}setOptions(g){this.options=g}solve(){let g,A,t,C;const I=this.body.nodes,e=this.physicsBody.physicsNodeIndices,i=this.physicsBody.forces;for(let o=0;o0){const g=I.edges.length+1,e=this.options.centralGravity*g*I.options.mass;C[I.id].x=A*e,C[I.id].y=t*e}}}class ZD extends MD{constructor(g,A,t){super(g,A,t),this._rng=Xu("FORCE ATLAS 2 BASED REPULSION SOLVER")}_calculateForces(g,A,t,C,I){0===g&&(A=g=.1*this._rng()),this.overlapAvoidanceFactor<1&&C.shape.radius&&(g=Math.max(.1+this.overlapAvoidanceFactor*C.shape.radius,g-C.shape.radius));const e=C.edges.length+1,i=this.options.gravitationalConstant*I.mass*C.options.mass*e/Math.pow(g,2),o=A*i,n=t*i;this.physicsBody.forces[C.id].x+=o,this.physicsBody.forces[C.id].y+=n}}class SD{constructor(g,A,t){this.body=g,this.physicsBody=A,this.setOptions(t)}setOptions(g){this.options=g,this.overlapAvoidanceFactor=Math.max(0,Math.min(1,this.options.avoidOverlap||0))}solve(){const g=this.body.nodes,A=this.physicsBody.physicsNodeIndices,t=this.physicsBody.forces,C=this.options.nodeDistance;for(let I=0;I{this.initPhysics()}),this.body.emitter.on("_layoutFailed",()=>{this.layoutFailed=!0}),this.body.emitter.on("resetPhysics",()=>{this.stopSimulation(),this.ready=!1}),this.body.emitter.on("disablePhysics",()=>{this.physicsEnabled=!1,this.stopSimulation()}),this.body.emitter.on("restorePhysics",()=>{this.setOptions(this.options),!0===this.ready&&this.startSimulation()}),this.body.emitter.on("startSimulation",()=>{!0===this.ready&&this.startSimulation()}),this.body.emitter.on("stopSimulation",()=>{this.stopSimulation()}),this.body.emitter.on("destroy",()=>{this.stopSimulation(!1),this.body.emitter.off()}),this.body.emitter.on("_dataChanged",()=>{this.updatePhysicsData()})}setOptions(g){if(void 0!==g)if(!1===g)this.options.enabled=!1,this.physicsEnabled=!1,this.stopSimulation();else if(!0===g)this.options.enabled=!0,this.physicsEnabled=!0,this.startSimulation();else{this.physicsEnabled=!0,np(["stabilization"],this.options,g),xp(this.options,g,"stabilization"),void 0===g.enabled&&(this.options.enabled=!0),!1===this.options.enabled&&(this.physicsEnabled=!1,this.stopSimulation());const A=this.options.wind;A&&"function"!=typeof A&&(("number"!=typeof A.x||RD(A.x))&&(A.x=0),("number"!=typeof A.y||RD(A.y))&&(A.y=0)),this.timestep=this.options.timestep}this.init()}init(){let g;"forceAtlas2Based"===this.options.solver?(g=this.options.forceAtlas2Based,this.nodesSolver=new ZD(this.body,this.physicsBody,g),this.edgesSolver=new jD(this.body,this.physicsBody,g),this.gravitySolver=new BD(this.body,this.physicsBody,g)):"repulsion"===this.options.solver?(g=this.options.repulsion,this.nodesSolver=new GD(this.body,this.physicsBody,g),this.edgesSolver=new jD(this.body,this.physicsBody,g),this.gravitySolver=new zD(this.body,this.physicsBody,g)):"hierarchicalRepulsion"===this.options.solver?(g=this.options.hierarchicalRepulsion,this.nodesSolver=new SD(this.body,this.physicsBody,g),this.edgesSolver=new FD(this.body,this.physicsBody,g),this.gravitySolver=new zD(this.body,this.physicsBody,g)):(g=this.options.barnesHut,this.nodesSolver=new MD(this.body,this.physicsBody,g),this.edgesSolver=new jD(this.body,this.physicsBody,g),this.gravitySolver=new zD(this.body,this.physicsBody,g)),this.modelOptions=g}initPhysics(){!0===this.physicsEnabled&&!0===this.options.enabled?!0===this.options.stabilization.enabled?this.stabilize():(this.stabilized=!1,this.ready=!0,this.body.emitter.emit("fit",{},this.layoutFailed),this.startSimulation()):(this.ready=!0,this.body.emitter.emit("fit"))}startSimulation(){var g;!0===this.physicsEnabled&&!0===this.options.enabled?(this.stabilized=!1,this.adaptiveTimestep=!1,this.body.emitter.emit("_resizeNodes"),void 0===this.viewFunction&&(this.viewFunction=IC(g=this.simulationStep).call(g,this),this.body.emitter.on("initRedraw",this.viewFunction),this.body.emitter.emit("_startRendering"))):this.body.emitter.emit("_redraw")}stopSimulation(){let g=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];this.stabilized=!0,!0===g&&this._emitStabilized(),void 0!==this.viewFunction&&(this.body.emitter.off("initRedraw",this.viewFunction),this.viewFunction=void 0,!0===g&&this.body.emitter.emit("_stopRendering"))}simulationStep(){const g=Aa();this.physicsTick();(Aa()-g<.4*this.simulationInterval||!0===this.runDoubleSpeed)&&!1===this.stabilized&&(this.physicsTick(),this.runDoubleSpeed=!0),!0===this.stabilized&&this.stopSimulation()}_emitStabilized(){let g=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.stabilizationIterations;(this.stabilizationIterations>1||!0===this.startedStabilization)&&$l(()=>{this.body.emitter.emit("stabilized",{iterations:g}),this.startedStabilization=!1,this.stabilizationIterations=0},0)}physicsStep(){this.gravitySolver.solve(),this.nodesSolver.solve(),this.edgesSolver.solve(),this.moveNodes()}adjustTimeStep(){!0===this._evaluateStepQuality()?this.timestep=1.2*this.timestep:this.timestep/1.2.3))return!1;return!0}moveNodes(){const g=this.physicsBody.physicsNodeIndices;let A=0,t=0;for(let C=0;CC&&(g=g>0?C:-C),g}_performStep(g){const A=this.body.nodes[g],t=this.physicsBody.forces[g];if(this.options.wind)if("function"==typeof this.options.wind){const A=this.options.wind(g);A&&"object"==typeof A&&("number"!=typeof A.x||RD(A.x)||(t.x+=A.x),"number"!=typeof A.y||RD(A.y)||(t.y+=A.y))}else t.x+=this.options.wind.x,t.y+=this.options.wind.y;const C=this.physicsBody.velocities[g];this.previousStates[g]={x:A.x,y:A.y,vx:C.x,vy:C.y},!1===A.options.fixed.x?(C.x=this.calculateComponentVelocity(C.x,t.x,A.options.mass),A.x+=C.x*this.timestep):(t.x=0,C.x=0),!1===A.options.fixed.y?(C.y=this.calculateComponentVelocity(C.y,t.y,A.options.mass),A.y+=C.y*this.timestep):(t.y=0,C.y=0);return Math.sqrt(Math.pow(C.x,2)+Math.pow(C.y,2))}_freezeNodes(){const g=this.body.nodes;for(const A in g)if(Object.prototype.hasOwnProperty.call(g,A)&&g[A].x&&g[A].y){const t=g[A].options.fixed;this.freezeCache[A]={x:t.x,y:t.y},t.x=!0,t.y=!0}}_restoreFrozenNodes(){const g=this.body.nodes;for(const A in g)Object.prototype.hasOwnProperty.call(g,A)&&void 0!==this.freezeCache[A]&&(g[A].options.fixed.x=this.freezeCache[A].x,g[A].options.fixed.y=this.freezeCache[A].y);this.freezeCache={}}stabilize(){let g=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.options.stabilization.iterations;"number"!=typeof g&&(g=this.options.stabilization.iterations,console.error("The stabilize method needs a numeric amount of iterations. Switching to default: ",g)),0!==this.physicsBody.physicsNodeIndices.length?(this.adaptiveTimestep=this.options.adaptiveTimestep,this.body.emitter.emit("_resizeNodes"),this.stopSimulation(),this.stabilized=!1,this.body.emitter.emit("_blockRedraw"),this.targetIterations=g,!0===this.options.stabilization.onlyDynamicEdges&&this._freezeNodes(),this.stabilizationIterations=0,$l(()=>this._stabilizationBatch(),0)):this.ready=!0}_startStabilizing(){return!0!==this.startedStabilization&&(this.body.emitter.emit("startStabilizing"),this.startedStabilization=!0,!0)}_stabilizationBatch(){const g=()=>!1===this.stabilized&&this.stabilizationIterations{this.body.emitter.emit("stabilizationProgress",{iterations:this.stabilizationIterations,total:this.targetIterations})};this._startStabilizing()&&A();let t=0;for(;g()&&t0&&void 0!==arguments[0]?arguments[0]:()=>{};fN(this,ON,new EN),fN(this,TN,new EN),fN(this,DN,void 0),vN(DN,this,g)}get sizeNodes(){return bN(ON,this).size}get sizeEdges(){return bN(TN,this).size}getNodes(){return bN(ON,this).getSelection()}getEdges(){return bN(TN,this).getSelection()}addNodes(){bN(ON,this).add(...arguments)}addEdges(){bN(TN,this).add(...arguments)}deleteNodes(g){bN(ON,this).delete(g)}deleteEdges(g){bN(TN,this).delete(g)}clear(){bN(ON,this).clear(),bN(TN,this).clear()}commit(){const g={nodes:bN(ON,this).commit(),edges:bN(TN,this).commit()};for(var A=arguments.length,t=new Array(A),C=0;C{this.updateSelection()})}setOptions(g){if(void 0!==g){op(["multiselect","hoverConnectedEdges","selectable","selectConnectedEdges"],this.options,g)}}selectOnPoint(g){let A=!1;if(!0===this.options.selectable){const t=this.getNodeAt(g)||this.getEdgeAt(g);this.unselectAll(),void 0!==t&&(A=this.selectObject(t)),this.body.emitter.emit("_requestRedraw")}return A}selectAdditionalOnPoint(g){let A=!1;if(!0===this.options.selectable){const t=this.getNodeAt(g)||this.getEdgeAt(g);void 0!==t&&(A=!0,!0===t.isSelected()?this.deselectObject(t):this.selectObject(t),this.body.emitter.emit("_requestRedraw"))}return A}_initBaseEvent(g,A){const t={};return t.pointer={DOM:{x:A.x,y:A.y},canvas:this.canvas.DOMtoCanvas(A)},t.event=g,t}generateClickEvent(g,A,t,C){let I=arguments.length>4&&void 0!==arguments[4]&&arguments[4];const e=this._initBaseEvent(A,t);if(!0===I)e.nodes=[],e.edges=[];else{const g=this.getSelection();e.nodes=g.nodes,e.edges=g.edges}void 0!==C&&(e.previousSelection=C),"click"==g&&(e.items=this.getClickedItems(t)),void 0!==A.controlEdge&&(e.controlEdge=A.controlEdge),this.body.emitter.emit(g,e)}selectObject(g){let A=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.options.selectConnectedEdges;return void 0!==g&&(g instanceof Wy?(!0===A&&this._selectionAccumulator.addEdges(...g.edges),this._selectionAccumulator.addNodes(g)):this._selectionAccumulator.addEdges(g),!0)}deselectObject(g){!0===g.isSelected()&&(g.selected=!1,this._removeFromSelection(g))}_getAllNodesOverlappingWith(g){const A=[],t=this.body.nodes;for(let C=0;C1&&void 0!==arguments[1])||arguments[1];const t=this._pointerToPositionObject(g),C=this._getAllNodesOverlappingWith(t);return C.length>0?!0===A?this.body.nodes[C[C.length-1]]:C[C.length-1]:void 0}_getEdgesOverlappingWith(g,A){const t=this.body.edges;for(let C=0;C1&&void 0!==arguments[1])||arguments[1];const t=this.canvas.DOMtoCanvas(g);let C=10,I=null;const e=this.body.edges;for(let g=0;g0&&(this.generateClickEvent("deselectEdge",A,g,I),t=!0),C.nodes.deleted.length>0&&(this.generateClickEvent("deselectNode",A,g,I),t=!0),C.nodes.added.length>0&&(this.generateClickEvent("selectNode",A,g),t=!0),C.edges.added.length>0&&(this.generateClickEvent("selectEdge",A,g),t=!0),!0===t&&this.generateClickEvent("select",A,g)}getSelection(){return{nodes:this.getSelectedNodeIds(),edges:this.getSelectedEdgeIds()}}getSelectedNodes(){return this._selectionAccumulator.getNodes()}getSelectedEdges(){return this._selectionAccumulator.getEdges()}getSelectedNodeIds(){var g;return Tr(g=this._selectionAccumulator.getNodes()).call(g,g=>g.id)}getSelectedEdgeIds(){var g;return Tr(g=this._selectionAccumulator.getEdges()).call(g,g=>g.id)}setSelection(g){let A=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(!g||!g.nodes&&!g.edges)throw new TypeError("Selection must be an object with nodes and/or edges properties");if((A.unselectAll||void 0===A.unselectAll)&&this.unselectAll(),g.nodes)for(const t of g.nodes){const g=this.body.nodes[t];if(!g)throw new RangeError('Node with id "'+t+'" not found');this.selectObject(g,A.highlightEdges)}if(g.edges)for(const A of g.edges){const g=this.body.edges[A];if(!g)throw new RangeError('Edge with id "'+A+'" not found');this.selectObject(g)}this.body.emitter.emit("_requestRedraw"),this._selectionAccumulator.commit()}selectNodes(g){let A=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];if(!g||void 0===g.length)throw"Selection must be an array with ids";this.setSelection({nodes:g},{highlightEdges:A})}selectEdges(g){if(!g||void 0===g.length)throw"Selection must be an array with ids";this.setSelection({edges:g})}updateSelection(){for(const g in this._selectionAccumulator.getNodes())Object.prototype.hasOwnProperty.call(this.body.nodes,g.id)||this._selectionAccumulator.deleteNodes(g);for(const g in this._selectionAccumulator.getEdges())Object.prototype.hasOwnProperty.call(this.body.edges,g.id)||this._selectionAccumulator.deleteEdges(g)}getClickedItems(g){const A=this.canvas.DOMtoCanvas(g),t=[],C=this.body.nodeIndices,I=this.body.nodes;for(let g=C.length-1;g>=0;g--){const e=I[C[g]].getItemsOnPoint(A);t.push.apply(t,e)}const e=this.body.edgeIndices,i=this.body.edges;for(let g=e.length-1;g>=0;g--){const C=i[e[g]].getItemsOnPoint(A);t.push.apply(t,C)}return t}}class RN{constructor(g,A){var t,C;this.body=g,this.canvas=A,this.animationSpeed=1/this.renderRefreshRate,this.animationEasingFunction="easeInOutQuint",this.easingTime=0,this.sourceScale=0,this.targetScale=0,this.sourceTranslation=0,this.targetTranslation=0,this.lockedOnNodeId=void 0,this.lockedOnNodeOffset=void 0,this.touchTime=0,this.viewFunction=void 0,this.body.emitter.on("fit",IC(t=this.fit).call(t,this)),this.body.emitter.on("animationFinished",()=>{this.body.emitter.emit("_stopRendering")}),this.body.emitter.on("unlockNode",IC(C=this.releaseNode).call(C,this))}setOptions(){let g=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.options=g}fit(g){let A=arguments.length>1&&void 0!==arguments[1]&&arguments[1];g=function(g,A){const t=_t({nodes:A,minZoomLevel:Number.MIN_VALUE,maxZoomLevel:1},null!=g?g:{});if(!cr(t.nodes))throw new TypeError("Nodes has to be an array of ids.");if(0===t.nodes.length&&(t.nodes=A),!("number"==typeof t.minZoomLevel&&t.minZoomLevel>0))throw new TypeError("Min zoom level has to be a number higher than zero.");if(!("number"==typeof t.maxZoomLevel&&t.minZoomLevel<=t.maxZoomLevel))throw new TypeError("Max zoom level has to be a number higher than min zoom level.");return t}(g,this.body.nodeIndices);const t=this.canvas.frame.canvas.clientWidth,C=this.canvas.frame.canvas.clientHeight;let I,e;if(0===t||0===C)e=1,I=Ff.getRange(this.body.nodes,g.nodes);else if(!0===A){let A=0;for(const g in this.body.nodes)if(Object.prototype.hasOwnProperty.call(this.body.nodes,g)){!0===this.body.nodes[g].predefinedPosition&&(A+=1)}if(A>.5*this.body.nodeIndices.length)return void this.fit(g,!1);I=Ff.getRange(this.body.nodes,g.nodes);e=12.662/(this.body.nodeIndices.length+7.4147)+.0964822;e*=Math.min(t/600,C/600)}else{this.body.emitter.emit("_resizeNodes"),I=Ff.getRange(this.body.nodes,g.nodes);const A=t/(1.1*Math.abs(I.maxX-I.minX)),i=C/(1.1*Math.abs(I.maxY-I.minY));e=A<=i?A:i}e>g.maxZoomLevel?e=g.maxZoomLevel:e1&&void 0!==arguments[1]?arguments[1]:{};if(void 0!==this.body.nodes[g]){const t={x:this.body.nodes[g].x,y:this.body.nodes[g].y};A.position=t,A.lockedOnNode=g,this.moveTo(A)}else console.error("Node: "+g+" cannot be found.")}moveTo(g){if(void 0!==g){if(null!=g.offset){if(null!=g.offset.x){if(g.offset.x=+g.offset.x,!mv(g.offset.x))throw new TypeError('The option "offset.x" has to be a finite number.')}else g.offset.x=0;if(null!=g.offset.y){if(g.offset.y=+g.offset.y,!mv(g.offset.y))throw new TypeError('The option "offset.y" has to be a finite number.')}else g.offset.x=0}else g.offset={x:0,y:0};if(null!=g.position){if(null!=g.position.x){if(g.position.x=+g.position.x,!mv(g.position.x))throw new TypeError('The option "position.x" has to be a finite number.')}else g.position.x=0;if(null!=g.position.y){if(g.position.y=+g.position.y,!mv(g.position.y))throw new TypeError('The option "position.y" has to be a finite number.')}else g.position.x=0}else g.position=this.getViewPosition();if(null!=g.scale){if(g.scale=+g.scale,!(g.scale>0))throw new TypeError('The option "scale" has to be a number greater than zero.')}else g.scale=this.body.view.scale;void 0===g.animation&&(g.animation={duration:0}),!1===g.animation&&(g.animation={duration:0}),!0===g.animation&&(g.animation={}),void 0===g.animation.duration&&(g.animation.duration=1e3),void 0===g.animation.easingFunction&&(g.animation.easingFunction="easeInOutQuad"),this.animateView(g)}else g={}}animateView(g){if(void 0===g)return;this.animationEasingFunction=g.animation.easingFunction,this.releaseNode(),!0===g.locked&&(this.lockedOnNodeId=g.lockedOnNode,this.lockedOnNodeOffset=g.offset),0!=this.easingTime&&this._transitionRedraw(!0),this.sourceScale=this.body.view.scale,this.sourceTranslation=this.body.view.translation,this.targetScale=g.scale,this.body.view.scale=this.targetScale;const A=this.canvas.DOMtoCanvas({x:.5*this.canvas.frame.canvas.clientWidth,y:.5*this.canvas.frame.canvas.clientHeight}),t=A.x-g.position.x,C=A.y-g.position.y;var I,e;(this.targetTranslation={x:this.sourceTranslation.x+t*this.targetScale+g.offset.x,y:this.sourceTranslation.y+C*this.targetScale+g.offset.y},0===g.animation.duration)?null!=this.lockedOnNodeId?(this.viewFunction=IC(I=this._lockedRedraw).call(I,this),this.body.emitter.on("initRedraw",this.viewFunction)):(this.body.view.scale=this.targetScale,this.body.view.translation=this.targetTranslation,this.body.emitter.emit("_requestRedraw")):(this.animationSpeed=1/(60*g.animation.duration*.001)||1/60,this.animationEasingFunction=g.animation.easingFunction,this.viewFunction=IC(e=this._transitionRedraw).call(e,this),this.body.emitter.on("initRedraw",this.viewFunction),this.body.emitter.emit("_startRendering"))}_lockedRedraw(){const g=this.body.nodes[this.lockedOnNodeId].x,A=this.body.nodes[this.lockedOnNodeId].y,t=this.canvas.DOMtoCanvas({x:.5*this.canvas.frame.canvas.clientWidth,y:.5*this.canvas.frame.canvas.clientHeight}),C=t.x-g,I=t.y-A,e=this.body.view.translation,i={x:e.x+C*this.body.view.scale+this.lockedOnNodeOffset.x,y:e.y+I*this.body.view.scale+this.lockedOnNodeOffset.y};this.body.view.translation=i}releaseNode(){void 0!==this.lockedOnNodeId&&void 0!==this.viewFunction&&(this.body.emitter.off("initRedraw",this.viewFunction),this.lockedOnNodeId=void 0,this.lockedOnNodeOffset=void 0)}_transitionRedraw(){let g=arguments.length>0&&void 0!==arguments[0]&&arguments[0];this.easingTime+=this.animationSpeed,this.easingTime=!0===g?1:this.easingTime;const A=Ep[this.animationEasingFunction](this.easingTime);if(this.body.view.scale=this.sourceScale+(this.targetScale-this.sourceScale)*A,this.body.view.translation={x:this.sourceTranslation.x+(this.targetTranslation.x-this.sourceTranslation.x)*A,y:this.sourceTranslation.y+(this.targetTranslation.y-this.sourceTranslation.y)*A},this.easingTime>=1){var t;if(this.body.emitter.off("initRedraw",this.viewFunction),this.easingTime=0,null!=this.lockedOnNodeId)this.viewFunction=IC(t=this._lockedRedraw).call(t,this),this.body.emitter.on("initRedraw",this.viewFunction);this.body.emitter.emit("animationFinished")}}getScale(){return this.body.view.scale}getViewPosition(){return this.canvas.DOMtoCanvas({x:.5*this.canvas.frame.canvas.clientWidth,y:.5*this.canvas.frame.canvas.clientHeight})}}const PN="string",MN="boolean",zN="number",BN="array",ZN="object",SN=["arrow","bar","box","circle","crow","curve","diamond","image","inv_curve","inv_triangle","triangle","vee"],FN={borderWidth:{number:zN},borderWidthSelected:{number:zN,undefined:"undefined"},brokenImage:{string:PN,undefined:"undefined"},chosen:{label:{boolean:MN,function:"function"},node:{boolean:MN,function:"function"},__type__:{object:ZN,boolean:MN}},color:{border:{string:PN},background:{string:PN},highlight:{border:{string:PN},background:{string:PN},__type__:{object:ZN,string:PN}},hover:{border:{string:PN},background:{string:PN},__type__:{object:ZN,string:PN}},__type__:{object:ZN,string:PN}},opacity:{number:zN,undefined:"undefined"},fixed:{x:{boolean:MN},y:{boolean:MN},__type__:{object:ZN,boolean:MN}},font:{align:{string:PN},color:{string:PN},size:{number:zN},face:{string:PN},background:{string:PN},strokeWidth:{number:zN},strokeColor:{string:PN},vadjust:{number:zN},multi:{boolean:MN,string:PN},bold:{color:{string:PN},size:{number:zN},face:{string:PN},mod:{string:PN},vadjust:{number:zN},__type__:{object:ZN,string:PN}},boldital:{color:{string:PN},size:{number:zN},face:{string:PN},mod:{string:PN},vadjust:{number:zN},__type__:{object:ZN,string:PN}},ital:{color:{string:PN},size:{number:zN},face:{string:PN},mod:{string:PN},vadjust:{number:zN},__type__:{object:ZN,string:PN}},mono:{color:{string:PN},size:{number:zN},face:{string:PN},mod:{string:PN},vadjust:{number:zN},__type__:{object:ZN,string:PN}},__type__:{object:ZN,string:PN}},group:{string:PN,number:zN,undefined:"undefined"},heightConstraint:{minimum:{number:zN},valign:{string:PN},__type__:{object:ZN,boolean:MN,number:zN}},hidden:{boolean:MN},icon:{face:{string:PN},code:{string:PN},size:{number:zN},color:{string:PN},weight:{string:PN,number:zN},__type__:{object:ZN}},id:{string:PN,number:zN},image:{selected:{string:PN,undefined:"undefined"},unselected:{string:PN,undefined:"undefined"},__type__:{object:ZN,string:PN}},imagePadding:{top:{number:zN},right:{number:zN},bottom:{number:zN},left:{number:zN},__type__:{object:ZN,number:zN}},label:{string:PN,undefined:"undefined"},labelHighlightBold:{boolean:MN},level:{number:zN,undefined:"undefined"},margin:{top:{number:zN},right:{number:zN},bottom:{number:zN},left:{number:zN},__type__:{object:ZN,number:zN}},mass:{number:zN},physics:{boolean:MN},scaling:{min:{number:zN},max:{number:zN},label:{enabled:{boolean:MN},min:{number:zN},max:{number:zN},maxVisible:{number:zN},drawThreshold:{number:zN},__type__:{object:ZN,boolean:MN}},customScalingFunction:{function:"function"},__type__:{object:ZN}},shadow:{enabled:{boolean:MN},color:{string:PN},size:{number:zN},x:{number:zN},y:{number:zN},__type__:{object:ZN,boolean:MN}},shape:{string:["custom","ellipse","circle","database","box","text","image","circularImage","diamond","dot","star","triangle","triangleDown","square","icon","hexagon"]},ctxRenderer:{function:"function"},shapeProperties:{borderDashes:{boolean:MN,array:BN},borderRadius:{number:zN},interpolation:{boolean:MN},useImageSize:{boolean:MN},useBorderWithImage:{boolean:MN},coordinateOrigin:{string:["center","top-left"]},__type__:{object:ZN}},size:{number:zN},title:{string:PN,dom:"dom",undefined:"undefined"},value:{number:zN,undefined:"undefined"},widthConstraint:{minimum:{number:zN},maximum:{number:zN},__type__:{object:ZN,boolean:MN,number:zN}},x:{number:zN},y:{number:zN},__type__:{object:ZN}},GN={configure:{enabled:{boolean:MN},filter:{boolean:MN,string:PN,array:BN,function:"function"},container:{dom:"dom"},showButton:{boolean:MN},__type__:{object:ZN,boolean:MN,string:PN,array:BN,function:"function"}},edges:{arrows:{to:{enabled:{boolean:MN},scaleFactor:{number:zN},type:{string:SN},imageHeight:{number:zN},imageWidth:{number:zN},src:{string:PN},__type__:{object:ZN,boolean:MN}},middle:{enabled:{boolean:MN},scaleFactor:{number:zN},type:{string:SN},imageWidth:{number:zN},imageHeight:{number:zN},src:{string:PN},__type__:{object:ZN,boolean:MN}},from:{enabled:{boolean:MN},scaleFactor:{number:zN},type:{string:SN},imageWidth:{number:zN},imageHeight:{number:zN},src:{string:PN},__type__:{object:ZN,boolean:MN}},__type__:{string:["from","to","middle"],object:ZN}},endPointOffset:{from:{number:zN},to:{number:zN},__type__:{object:ZN,number:zN}},arrowStrikethrough:{boolean:MN},background:{enabled:{boolean:MN},color:{string:PN},size:{number:zN},dashes:{boolean:MN,array:BN},__type__:{object:ZN,boolean:MN}},chosen:{label:{boolean:MN,function:"function"},edge:{boolean:MN,function:"function"},__type__:{object:ZN,boolean:MN}},color:{color:{string:PN},highlight:{string:PN},hover:{string:PN},inherit:{string:["from","to","both"],boolean:MN},opacity:{number:zN},__type__:{object:ZN,string:PN}},dashes:{boolean:MN,array:BN},font:{color:{string:PN},size:{number:zN},face:{string:PN},background:{string:PN},strokeWidth:{number:zN},strokeColor:{string:PN},align:{string:["horizontal","top","middle","bottom"]},vadjust:{number:zN},multi:{boolean:MN,string:PN},bold:{color:{string:PN},size:{number:zN},face:{string:PN},mod:{string:PN},vadjust:{number:zN},__type__:{object:ZN,string:PN}},boldital:{color:{string:PN},size:{number:zN},face:{string:PN},mod:{string:PN},vadjust:{number:zN},__type__:{object:ZN,string:PN}},ital:{color:{string:PN},size:{number:zN},face:{string:PN},mod:{string:PN},vadjust:{number:zN},__type__:{object:ZN,string:PN}},mono:{color:{string:PN},size:{number:zN},face:{string:PN},mod:{string:PN},vadjust:{number:zN},__type__:{object:ZN,string:PN}},__type__:{object:ZN,string:PN}},hidden:{boolean:MN},hoverWidth:{function:"function",number:zN},label:{string:PN,undefined:"undefined"},labelHighlightBold:{boolean:MN},length:{number:zN,undefined:"undefined"},physics:{boolean:MN},scaling:{min:{number:zN},max:{number:zN},label:{enabled:{boolean:MN},min:{number:zN},max:{number:zN},maxVisible:{number:zN},drawThreshold:{number:zN},__type__:{object:ZN,boolean:MN}},customScalingFunction:{function:"function"},__type__:{object:ZN}},selectionWidth:{function:"function",number:zN},selfReferenceSize:{number:zN},selfReference:{size:{number:zN},angle:{number:zN},renderBehindTheNode:{boolean:MN},__type__:{object:ZN}},shadow:{enabled:{boolean:MN},color:{string:PN},size:{number:zN},x:{number:zN},y:{number:zN},__type__:{object:ZN,boolean:MN}},smooth:{enabled:{boolean:MN},type:{string:["dynamic","continuous","discrete","diagonalCross","straightCross","horizontal","vertical","curvedCW","curvedCCW","cubicBezier"]},roundness:{number:zN},forceDirection:{string:["horizontal","vertical","none"],boolean:MN},__type__:{object:ZN,boolean:MN}},title:{string:PN,undefined:"undefined"},width:{number:zN},widthConstraint:{maximum:{number:zN},__type__:{object:ZN,boolean:MN,number:zN}},value:{number:zN,undefined:"undefined"},__type__:{object:ZN}},groups:{useDefaultGroups:{boolean:MN},__any__:FN,__type__:{object:ZN}},interaction:{dragNodes:{boolean:MN},dragView:{boolean:MN},hideEdgesOnDrag:{boolean:MN},hideEdgesOnZoom:{boolean:MN},hideNodesOnDrag:{boolean:MN},hover:{boolean:MN},keyboard:{enabled:{boolean:MN},speed:{x:{number:zN},y:{number:zN},zoom:{number:zN},__type__:{object:ZN}},bindToWindow:{boolean:MN},autoFocus:{boolean:MN},__type__:{object:ZN,boolean:MN}},multiselect:{boolean:MN},navigationButtons:{boolean:MN},selectable:{boolean:MN},selectConnectedEdges:{boolean:MN},hoverConnectedEdges:{boolean:MN},tooltipDelay:{number:zN},zoomView:{boolean:MN},zoomSpeed:{number:zN},__type__:{object:ZN}},layout:{randomSeed:{undefined:"undefined",number:zN,string:PN},improvedLayout:{boolean:MN},clusterThreshold:{number:zN},hierarchical:{enabled:{boolean:MN},levelSeparation:{number:zN},nodeSpacing:{number:zN},treeSpacing:{number:zN},blockShifting:{boolean:MN},edgeMinimization:{boolean:MN},parentCentralization:{boolean:MN},direction:{string:["UD","DU","LR","RL"]},sortMethod:{string:["hubsize","directed"]},shakeTowards:{string:["leaves","roots"]},__type__:{object:ZN,boolean:MN}},__type__:{object:ZN}},manipulation:{enabled:{boolean:MN},initiallyActive:{boolean:MN},addNode:{boolean:MN,function:"function"},addEdge:{boolean:MN,function:"function"},editNode:{function:"function"},editEdge:{editWithoutDrag:{function:"function"},__type__:{object:ZN,boolean:MN,function:"function"}},deleteNode:{boolean:MN,function:"function"},deleteEdge:{boolean:MN,function:"function"},controlNodeStyle:FN,__type__:{object:ZN,boolean:MN}},nodes:FN,physics:{enabled:{boolean:MN},barnesHut:{theta:{number:zN},gravitationalConstant:{number:zN},centralGravity:{number:zN},springLength:{number:zN},springConstant:{number:zN},damping:{number:zN},avoidOverlap:{number:zN},__type__:{object:ZN}},forceAtlas2Based:{theta:{number:zN},gravitationalConstant:{number:zN},centralGravity:{number:zN},springLength:{number:zN},springConstant:{number:zN},damping:{number:zN},avoidOverlap:{number:zN},__type__:{object:ZN}},repulsion:{centralGravity:{number:zN},springLength:{number:zN},springConstant:{number:zN},nodeDistance:{number:zN},damping:{number:zN},__type__:{object:ZN}},hierarchicalRepulsion:{centralGravity:{number:zN},springLength:{number:zN},springConstant:{number:zN},nodeDistance:{number:zN},damping:{number:zN},avoidOverlap:{number:zN},__type__:{object:ZN}},maxVelocity:{number:zN},minVelocity:{number:zN},solver:{string:["barnesHut","repulsion","hierarchicalRepulsion","forceAtlas2Based"]},stabilization:{enabled:{boolean:MN},iterations:{number:zN},updateInterval:{number:zN},onlyDynamicEdges:{boolean:MN},fit:{boolean:MN},__type__:{object:ZN,boolean:MN}},timestep:{number:zN},adaptiveTimestep:{boolean:MN},wind:{x:{number:zN},y:{number:zN},__type__:{object:ZN,function:"function"}},__type__:{object:ZN,boolean:MN}},autoResize:{boolean:MN},clickToUse:{boolean:MN},locale:{string:PN},locales:{__any__:{any:"any"},__type__:{object:ZN}},height:{string:PN},width:{string:PN},__type__:{object:ZN}},jN={nodes:{borderWidth:[1,0,10,1],borderWidthSelected:[2,0,10,1],color:{border:["color","#2B7CE9"],background:["color","#97C2FC"],highlight:{border:["color","#2B7CE9"],background:["color","#D2E5FF"]},hover:{border:["color","#2B7CE9"],background:["color","#D2E5FF"]}},opacity:[0,0,1,.1],fixed:{x:!1,y:!1},font:{color:["color","#343434"],size:[14,0,100,1],face:["arial","verdana","tahoma"],background:["color","none"],strokeWidth:[0,0,50,1],strokeColor:["color","#ffffff"]},hidden:!1,labelHighlightBold:!0,physics:!0,scaling:{min:[10,0,200,1],max:[30,0,200,1],label:{enabled:!1,min:[14,0,200,1],max:[30,0,200,1],maxVisible:[30,0,200,1],drawThreshold:[5,0,20,1]}},shadow:{enabled:!1,color:"rgba(0,0,0,0.5)",size:[10,0,20,1],x:[5,-30,30,1],y:[5,-30,30,1]},shape:["ellipse","box","circle","database","diamond","dot","square","star","text","triangle","triangleDown","hexagon"],shapeProperties:{borderDashes:!1,borderRadius:[6,0,20,1],interpolation:!0,useImageSize:!1},size:[25,0,200,1]},edges:{arrows:{to:{enabled:!1,scaleFactor:[1,0,3,.05],type:"arrow"},middle:{enabled:!1,scaleFactor:[1,0,3,.05],type:"arrow"},from:{enabled:!1,scaleFactor:[1,0,3,.05],type:"arrow"}},endPointOffset:{from:[0,-10,10,1],to:[0,-10,10,1]},arrowStrikethrough:!0,color:{color:["color","#848484"],highlight:["color","#848484"],hover:["color","#848484"],inherit:["from","to","both",!0,!1],opacity:[1,0,1,.05]},dashes:!1,font:{color:["color","#343434"],size:[14,0,100,1],face:["arial","verdana","tahoma"],background:["color","none"],strokeWidth:[2,0,50,1],strokeColor:["color","#ffffff"],align:["horizontal","top","middle","bottom"]},hidden:!1,hoverWidth:[1.5,0,5,.1],labelHighlightBold:!0,physics:!0,scaling:{min:[1,0,100,1],max:[15,0,100,1],label:{enabled:!0,min:[14,0,200,1],max:[30,0,200,1],maxVisible:[30,0,200,1],drawThreshold:[5,0,20,1]}},selectionWidth:[1.5,0,5,.1],selfReferenceSize:[20,0,200,1],selfReference:{size:[20,0,200,1],angle:[Math.PI/2,-6*Math.PI,6*Math.PI,Math.PI/8],renderBehindTheNode:!0},shadow:{enabled:!1,color:"rgba(0,0,0,0.5)",size:[10,0,20,1],x:[5,-30,30,1],y:[5,-30,30,1]},smooth:{enabled:!0,type:["dynamic","continuous","discrete","diagonalCross","straightCross","horizontal","vertical","curvedCW","curvedCCW","cubicBezier"],forceDirection:["horizontal","vertical","none"],roundness:[.5,0,1,.05]},width:[1,0,30,1]},layout:{hierarchical:{enabled:!1,levelSeparation:[150,20,500,5],nodeSpacing:[100,20,500,5],treeSpacing:[200,20,500,5],blockShifting:!0,edgeMinimization:!0,parentCentralization:!0,direction:["UD","DU","LR","RL"],sortMethod:["hubsize","directed"],shakeTowards:["leaves","roots"]}},interaction:{dragNodes:!0,dragView:!0,hideEdgesOnDrag:!1,hideEdgesOnZoom:!1,hideNodesOnDrag:!1,hover:!1,keyboard:{enabled:!1,speed:{x:[10,0,40,1],y:[10,0,40,1],zoom:[.02,0,.1,.005]},bindToWindow:!0,autoFocus:!0},multiselect:!1,navigationButtons:!1,selectable:!0,selectConnectedEdges:!0,hoverConnectedEdges:!0,tooltipDelay:[300,0,1e3,25],zoomView:!0,zoomSpeed:[1,.1,2,.1]},manipulation:{enabled:!1,initiallyActive:!1},physics:{enabled:!0,barnesHut:{theta:[.5,.1,1,.05],gravitationalConstant:[-2e3,-3e4,0,50],centralGravity:[.3,0,10,.05],springLength:[95,0,500,5],springConstant:[.04,0,1.2,.005],damping:[.09,0,1,.01],avoidOverlap:[0,0,1,.01]},forceAtlas2Based:{theta:[.5,.1,1,.05],gravitationalConstant:[-50,-500,0,1],centralGravity:[.01,0,1,.005],springLength:[95,0,500,5],springConstant:[.08,0,1.2,.005],damping:[.4,0,1,.01],avoidOverlap:[0,0,1,.01]},repulsion:{centralGravity:[.2,0,10,.05],springLength:[200,0,500,5],springConstant:[.05,0,1.2,.005],nodeDistance:[100,0,500,5],damping:[.09,0,1,.01]},hierarchicalRepulsion:{centralGravity:[.2,0,10,.05],springLength:[100,0,500,5],springConstant:[.01,0,1.2,.005],nodeDistance:[120,0,500,5],damping:[.09,0,1,.01],avoidOverlap:[0,0,1,.01]},maxVelocity:[50,0,150,1],minVelocity:[.1,.01,.5,.01],solver:["barnesHut","forceAtlas2Based","repulsion","hierarchicalRepulsion"],timestep:[.5,.01,1,.01],wind:{x:[0,-10,10,.1],y:[0,-10,10,.1]}}},LN=(g,A,t)=>{var C,I;return!(!bd(g).call(g,"physics")||!bd(C=jN.physics.solver).call(C,A)||t.physics.solver===A||"wind"===A)||!(!bd(g).call(g,"physics")||!bd(g).call(g,"wind")||"x"!==A&&"y"!==A||"function"!=typeof(null==t||null===(I=t.physics)||void 0===I?void 0:I.wind))};var VN=Object.freeze({__proto__:null,allOptions:GN,configuratorHideOption:LN,configureOptions:jN});function YN(g,A,t){var C,I,e,i;if(!(this instanceof YN))throw new SyntaxError("Constructor must be called with the new operator");this.options={},this.defaultOptions={locale:"en",locales:Df,clickToUse:!1},_t(this.options,this.defaultOptions),this.body={container:g,nodes:{},nodeIndices:[],edges:{},edgeIndices:[],emitter:{on:IC(C=this.on).call(C,this),off:IC(I=this.off).call(I,this),emit:IC(e=this.emit).call(e,this),once:IC(i=this.once).call(i,this)},eventListeners:{onTap:function(){},onTouch:function(){},onDoubleTap:function(){},onHold:function(){},onDragStart:function(){},onDrag:function(){},onDragEnd:function(){},onMouseWheel:function(){},onPinch:function(){},onMouseMove:function(){},onRelease:function(){},onContext:function(){}},data:{nodes:null,edges:null},functions:{createNode:function(){},createEdge:function(){},getPointer:function(){}},modules:{},view:{scale:1,translation:{x:0,y:0}},selectionBox:{show:!1,position:{start:{x:0,y:0},end:{x:0,y:0}}}},this.bindEventListeners(),this.images=new wf(()=>this.body.emitter.emit("_requestRedraw")),this.groups=new HT,this.canvas=new Mf(this.body),this.selectionHandler=new kN(this.body,this.canvas),this.interactionHandler=new JT(this.body,this.canvas,this.selectionHandler),this.view=new RN(this.body,this.canvas),this.renderer=new zf(this.body,this.canvas),this.physics=new LD(this.body),this.layoutEngine=new bD(this.body),this.clustering=new Uy(this.body),this.manipulation=new mD(this.body,this.canvas,this.selectionHandler,this.interactionHandler),this.nodesHandler=new PD(this.body,this.images,this.groups,this.layoutEngine),this.edgesHandler=new KT(this.body,this.images,this.groups),this.body.modules.kamadaKawai=new $T(this.body,150,.05),this.body.modules.clustering=this.clustering,this.canvas._create(),this.setOptions(t),this.setData(A)}TC(YN.prototype),YN.prototype.setOptions=function(g){if(null===g&&(g=void 0),void 0!==g){!0===Fp.validate(g,GN)&&console.error("%cErrors have been found in the supplied options object.",Sp);if(op(["locale","locales","clickToUse"],this.options,g),void 0!==g.locale&&(g.locale=function(g,A){try{const C=js(A.split(/[-_ /]/,2),2),I=C[0],e=C[1],i=null!=I?I.toLowerCase():null,o=null!=e?e.toUpperCase():null;if(i&&o){const A=i+"-"+o;if(Object.prototype.hasOwnProperty.call(g,A))return A;var t;console.warn(Qd(t="Unknown variant ".concat(o," of language ")).call(t,i,"."))}if(i){const A=i;if(Object.prototype.hasOwnProperty.call(g,A))return A;console.warn("Unknown language ".concat(i))}return console.warn("Unknown locale ".concat(A,", falling back to English.")),"en"}catch(g){return console.error(g),console.warn("Unexpected error while normalizing locale ".concat(A,", falling back to English.")),"en"}}(g.locales||this.options.locales,g.locale)),g=this.layoutEngine.setOptions(g.layout,g),this.canvas.setOptions(g),this.groups.setOptions(g.groups),this.nodesHandler.setOptions(g.nodes),this.edgesHandler.setOptions(g.edges),this.physics.setOptions(g.physics),this.manipulation.setOptions(g.manipulation,g,this.options),this.interactionHandler.setOptions(g.interaction),this.renderer.setOptions(g.interaction),this.selectionHandler.setOptions(g.interaction),void 0!==g.groups&&this.body.emitter.emit("refreshNodes"),"configure"in g&&(this.configurator||(this.configurator=new zp(this,this.body.container,jN,this.canvas.pixelRatio,LN)),this.configurator.setOptions(g.configure)),this.configurator&&!0===this.configurator.options.enabled){const g={nodes:{},edges:{},layout:{},interaction:{},manipulation:{},physics:{},global:{}};sp(g.nodes,this.nodesHandler.options),sp(g.edges,this.edgesHandler.options),sp(g.layout,this.layoutEngine.options),sp(g.interaction,this.selectionHandler.options),sp(g.interaction,this.renderer.options),sp(g.interaction,this.interactionHandler.options),sp(g.manipulation,this.manipulation.options),sp(g.physics,this.physics.options),sp(g.global,this.canvas.options),sp(g.global,this.options),this.configurator.setModuleOptions(g)}void 0!==g.clickToUse?!0===g.clickToUse?void 0===this.activator&&(this.activator=new Mp(this.canvas.frame),this.activator.on("change",()=>{this.body.emitter.emit("activate")})):(void 0!==this.activator&&(this.activator.destroy(),delete this.activator),this.body.emitter.emit("activate")):this.body.emitter.emit("activate"),this.canvas.setSize(),this.body.emitter.emit("startSimulation")}},YN.prototype._updateVisibleIndices=function(){const g=this.body.nodes,A=this.body.edges;this.body.nodeIndices=[],this.body.edgeIndices=[];for(const A in g)Object.prototype.hasOwnProperty.call(g,A)&&(this.clustering._isClusteredNode(A)||!1!==g[A].options.hidden||this.body.nodeIndices.push(g[A].id));for(const t in A)if(Object.prototype.hasOwnProperty.call(A,t)){const C=A[t],I=g[C.fromId],e=g[C.toId],i=void 0!==I&&void 0!==e;!this.clustering._isClusteredEdge(t)&&!1===C.options.hidden&&i&&!1===I.options.hidden&&!1===e.options.hidden&&this.body.edgeIndices.push(C.id)}},YN.prototype.bindEventListeners=function(){this.body.emitter.on("_dataChanged",()=>{this.edgesHandler._updateState(),this.body.emitter.emit("_dataUpdated")}),this.body.emitter.on("_dataUpdated",()=>{this.clustering._updateState(),this._updateVisibleIndices(),this._updateValueRange(this.body.nodes),this._updateValueRange(this.body.edges),this.body.emitter.emit("startSimulation"),this.body.emitter.emit("_requestRedraw")})},YN.prototype.setData=function(g){if(this.body.emitter.emit("resetPhysics"),this.body.emitter.emit("_resetData"),this.selectionHandler.unselectAll(),g&&g.dot&&(g.nodes||g.edges))throw new SyntaxError('Data must contain either parameter "dot" or parameter pair "nodes" and "edges", but not both.');if(this.setOptions(g&&g.options),g&&g.dot){console.warn("The dot property has been deprecated. Please use the static convertDot method to convert DOT into vis.network format and use the normal data format with nodes and edges. This converter is used like this: var data = vis.network.convertDot(dotString);");const A=pf(g.dot);return void this.setData(A)}if(g&&g.gephi){console.warn("The gephi property has been deprecated. Please use the static convertGephi method to convert gephi into vis.network format and use the normal data format with nodes and edges. This converter is used like this: var data = vis.network.convertGephi(gephiJson);");const A=bf(g.gephi);return void this.setData(A)}this.nodesHandler.setData(g&&g.nodes,!0),this.edgesHandler.setData(g&&g.edges,!0),this.body.emitter.emit("_dataChanged"),this.body.emitter.emit("_dataLoaded"),this.body.emitter.emit("initPhysics")},YN.prototype.destroy=function(){this.body.emitter.emit("destroy"),this.body.emitter.off(),this.off(),delete this.groups,delete this.canvas,delete this.selectionHandler,delete this.interactionHandler,delete this.view,delete this.renderer,delete this.physics,delete this.layoutEngine,delete this.clustering,delete this.manipulation,delete this.nodesHandler,delete this.edgesHandler,delete this.configurator,delete this.images;for(const g in this.body.nodes)Object.prototype.hasOwnProperty.call(this.body.nodes,g)&&delete this.body.nodes[g];for(const g in this.body.edges)Object.prototype.hasOwnProperty.call(this.body.edges,g)&&delete this.body.edges[g];tp(this.body.container)},YN.prototype._updateValueRange=function(g){let A,t,C,I=0;for(A in g)if(Object.prototype.hasOwnProperty.call(g,A)){const e=g[A].getValue();void 0!==e&&(t=void 0===t?e:Math.min(e,t),C=void 0===C?e:Math.max(e,C),I+=e)}if(void 0!==t&&void 0!==C)for(A in g)Object.prototype.hasOwnProperty.call(g,A)&&g[A].setValueRange(t,C,I)},YN.prototype.isActive=function(){return!this.activator||this.activator.active},YN.prototype.setSize=function(){return this.canvas.setSize.apply(this.canvas,arguments)},YN.prototype.canvasToDOM=function(){return this.canvas.canvasToDOM.apply(this.canvas,arguments)},YN.prototype.DOMtoCanvas=function(){return this.canvas.DOMtoCanvas.apply(this.canvas,arguments)},YN.prototype.findNode=function(){return this.clustering.findNode.apply(this.clustering,arguments)},YN.prototype.isCluster=function(){return this.clustering.isCluster.apply(this.clustering,arguments)},YN.prototype.openCluster=function(){return this.clustering.openCluster.apply(this.clustering,arguments)},YN.prototype.cluster=function(){return this.clustering.cluster.apply(this.clustering,arguments)},YN.prototype.getNodesInCluster=function(){return this.clustering.getNodesInCluster.apply(this.clustering,arguments)},YN.prototype.clusterByConnection=function(){return this.clustering.clusterByConnection.apply(this.clustering,arguments)},YN.prototype.clusterByHubsize=function(){return this.clustering.clusterByHubsize.apply(this.clustering,arguments)},YN.prototype.updateClusteredNode=function(){return this.clustering.updateClusteredNode.apply(this.clustering,arguments)},YN.prototype.getClusteredEdges=function(){return this.clustering.getClusteredEdges.apply(this.clustering,arguments)},YN.prototype.getBaseEdge=function(){return this.clustering.getBaseEdge.apply(this.clustering,arguments)},YN.prototype.getBaseEdges=function(){return this.clustering.getBaseEdges.apply(this.clustering,arguments)},YN.prototype.updateEdge=function(){return this.clustering.updateEdge.apply(this.clustering,arguments)},YN.prototype.clusterOutliers=function(){return this.clustering.clusterOutliers.apply(this.clustering,arguments)},YN.prototype.getSeed=function(){return this.layoutEngine.getSeed.apply(this.layoutEngine,arguments)},YN.prototype.enableEditMode=function(){return this.manipulation.enableEditMode.apply(this.manipulation,arguments)},YN.prototype.disableEditMode=function(){return this.manipulation.disableEditMode.apply(this.manipulation,arguments)},YN.prototype.addNodeMode=function(){return this.manipulation.addNodeMode.apply(this.manipulation,arguments)},YN.prototype.editNode=function(){return this.manipulation.editNode.apply(this.manipulation,arguments)},YN.prototype.editNodeMode=function(){return console.warn("Deprecated: Please use editNode instead of editNodeMode."),this.manipulation.editNode.apply(this.manipulation,arguments)},YN.prototype.addEdgeMode=function(){return this.manipulation.addEdgeMode.apply(this.manipulation,arguments)},YN.prototype.editEdgeMode=function(){return this.manipulation.editEdgeMode.apply(this.manipulation,arguments)},YN.prototype.deleteSelected=function(){return this.manipulation.deleteSelected.apply(this.manipulation,arguments)},YN.prototype.getPositions=function(){return this.nodesHandler.getPositions.apply(this.nodesHandler,arguments)},YN.prototype.getPosition=function(){return this.nodesHandler.getPosition.apply(this.nodesHandler,arguments)},YN.prototype.storePositions=function(){return this.nodesHandler.storePositions.apply(this.nodesHandler,arguments)},YN.prototype.moveNode=function(){return this.nodesHandler.moveNode.apply(this.nodesHandler,arguments)},YN.prototype.getBoundingBox=function(){return this.nodesHandler.getBoundingBox.apply(this.nodesHandler,arguments)},YN.prototype.getConnectedNodes=function(g){return void 0!==this.body.nodes[g]?this.nodesHandler.getConnectedNodes.apply(this.nodesHandler,arguments):this.edgesHandler.getConnectedNodes.apply(this.edgesHandler,arguments)},YN.prototype.getConnectedEdges=function(){return this.nodesHandler.getConnectedEdges.apply(this.nodesHandler,arguments)},YN.prototype.startSimulation=function(){return this.physics.startSimulation.apply(this.physics,arguments)},YN.prototype.stopSimulation=function(){return this.physics.stopSimulation.apply(this.physics,arguments)},YN.prototype.stabilize=function(){return this.physics.stabilize.apply(this.physics,arguments)},YN.prototype.getSelection=function(){return this.selectionHandler.getSelection.apply(this.selectionHandler,arguments)},YN.prototype.setSelection=function(){return this.selectionHandler.setSelection.apply(this.selectionHandler,arguments)},YN.prototype.getSelectedNodes=function(){return this.selectionHandler.getSelectedNodeIds.apply(this.selectionHandler,arguments)},YN.prototype.getSelectedEdges=function(){return this.selectionHandler.getSelectedEdgeIds.apply(this.selectionHandler,arguments)},YN.prototype.getNodeAt=function(){const g=this.selectionHandler.getNodeAt.apply(this.selectionHandler,arguments);return void 0!==g&&void 0!==g.id?g.id:g},YN.prototype.getEdgeAt=function(){const g=this.selectionHandler.getEdgeAt.apply(this.selectionHandler,arguments);return void 0!==g&&void 0!==g.id?g.id:g},YN.prototype.selectNodes=function(){return this.selectionHandler.selectNodes.apply(this.selectionHandler,arguments)},YN.prototype.selectEdges=function(){return this.selectionHandler.selectEdges.apply(this.selectionHandler,arguments)},YN.prototype.unselectAll=function(){this.selectionHandler.unselectAll.apply(this.selectionHandler,arguments),this.selectionHandler.commitWithoutEmitting.apply(this.selectionHandler),this.redraw()},YN.prototype.redraw=function(){return this.renderer.redraw.apply(this.renderer,arguments)},YN.prototype.getScale=function(){return this.view.getScale.apply(this.view,arguments)},YN.prototype.getViewPosition=function(){return this.view.getViewPosition.apply(this.view,arguments)},YN.prototype.fit=function(){return this.view.fit.apply(this.view,arguments)},YN.prototype.moveTo=function(){return this.view.moveTo.apply(this.view,arguments)},YN.prototype.focus=function(){return this.view.focus.apply(this.view,arguments)},YN.prototype.releaseNode=function(){return this.view.releaseNode.apply(this.view,arguments)},YN.prototype.getOptionsFromConfigurator=function(){let g={};return this.configurator&&(g=this.configurator.getOptions.apply(this.configurator)),g};const WN=pf;g.DataSet=VT,g.DataView=YT,g.Network=YN,g.NetworkImages=wf,g.Queue=GT,g.data=UT,g.networkDOTParser=vf,g.networkGephiParser=mf,g.networkOptions=VN,g.parseDOTNetwork=WN,g.parseGephiNetwork=bf}); +//# sourceMappingURL=vis-network.min.js.map diff --git a/src/mpe_lkg/store.py b/src/mpe_lkg/store.py new file mode 100644 index 0000000..a131903 --- /dev/null +++ b/src/mpe_lkg/store.py @@ -0,0 +1,164 @@ +"""SQLite-backed embedding store with brute-force similarity search. + +This replaces the Annoy index the project used to carry. An approximate +nearest-neighbour index earns its keep somewhere around a hundred thousand vectors; +this application holds roughly ten per query. Below that crossover a full scan in +numpy is both faster and exact, and dropping the dependency also removes the one +package in the requirements that does not ship a wheel for current Python -- which +is why ``pip install -r requirements.txt`` failed outright on a modern interpreter. + +It also fixes a correctness bug that came with the index: Annoy's angular distance +is ``sqrt(2 * (1 - cos))`` over the range [0, 2], and the old code reported +``1 - distance`` as though it were a cosine similarity. That number could go +negative and was displayed to four decimal places as if it meant something. +""" + +from __future__ import annotations + +import sqlite3 + +import numpy as np + +DEFAULT_PATH = "embeddings.db" + +SCHEMA = """ +CREATE TABLE IF NOT EXISTS embeddings ( + id INTEGER PRIMARY KEY, + text TEXT NOT NULL, + embedding BLOB NOT NULL, + is_question INTEGER NOT NULL DEFAULT 0, + dim INTEGER NOT NULL, + model TEXT NOT NULL DEFAULT '' +) +""" + + +class EmbeddingStore: + def __init__(self, path: str = DEFAULT_PATH) -> None: + self.path = path + # The rows are produced inside a streaming response, which Flask may run on + # a different thread than the one that opened the connection. + self.conn = sqlite3.connect(path, check_same_thread=False) + self.conn.execute(SCHEMA) + self._migrate() + self.conn.commit() + # (dim, model) -> (matrix, metadata). Dropped on write. + self._cache: dict[tuple[int, str], tuple[np.ndarray, list]] = {} + + def _migrate(self) -> None: + """Add the columns that older databases from this project lack. + + Without ``dim`` and ``model`` there is no way to tell a 4096-dimensional + vector from a 384-dimensional one, so switching embedding model silently + corrupted every search against the old rows. + """ + existing = {row[1] for row in self.conn.execute("PRAGMA table_info(embeddings)")} + if "dim" not in existing: + self.conn.execute("ALTER TABLE embeddings ADD COLUMN dim INTEGER NOT NULL DEFAULT 0") + if "model" not in existing: + self.conn.execute("ALTER TABLE embeddings ADD COLUMN model TEXT NOT NULL DEFAULT ''") + + def close(self) -> None: + self.conn.close() + + def clear(self) -> None: + self.conn.execute("DELETE FROM embeddings") + self.conn.commit() + self._cache.clear() + + def count(self) -> int: + return int(self.conn.execute("SELECT COUNT(*) FROM embeddings").fetchone()[0]) + + def add(self, text: str, embedding: np.ndarray, *, is_question: bool = False, model: str = "") -> int: + vector = np.asarray(embedding, dtype=np.float32).ravel() + cursor = self.conn.execute( + "INSERT INTO embeddings (text, embedding, is_question, dim, model) VALUES (?, ?, ?, ?, ?)", + (text, sqlite3.Binary(vector.tobytes()), int(is_question), int(vector.size), model), + ) + self.conn.commit() + self._cache.pop((int(vector.size), model), None) + return int(cursor.lastrowid) + + def _matrix(self, dim: int, model: str) -> tuple[np.ndarray, list]: + """Rows of the given shape as one contiguous matrix, cached in memory. + + Reading and unpacking every blob out of SQLite on each query is what makes a + growing store slow -- not the arithmetic. The matrix product over a hundred + thousand vectors takes about three milliseconds; decoding them from the + database each time does not. + """ + key = (dim, model) + if key in self._cache: + return self._cache[key] + + params: list = [dim] + sql = "SELECT id, text, embedding, is_question FROM embeddings WHERE dim = ?" + if model: + sql += " AND model = ?" + params.append(model) + rows = list(self.conn.execute(sql, params)) + + if rows: + matrix = np.vstack([np.frombuffer(r[2], dtype=np.float32) for r in rows]) + norms = np.linalg.norm(matrix, axis=1, keepdims=True) + matrix = np.divide(matrix, norms, out=np.zeros_like(matrix), where=norms > 0) + else: + matrix = np.zeros((0, dim), dtype=np.float32) + + meta = [(int(r[0]), r[1], bool(r[3])) for r in rows] + self._cache[key] = (matrix, meta) + return matrix, meta + + def find_similar( + self, + query: np.ndarray, + *, + top_k: int = 5, + model: str = "", + exclude_ids: set[int] | None = None, + ) -> list[dict]: + """Exact cosine nearest neighbours, restricted to compatible vectors. + + Exact, not approximate, and deliberately so. Measured on this machine at 768 + dimensions (``scripts/bench_search.py``), one query costs 0.017 ms over a + thousand vectors and 3.3 ms over a hundred thousand -- against an LLM call + that takes seconds. An approximate index answers in 0.03 ms flat, which buys + nothing here, and the one this project used to depend on cannot be appended + to: it has to be rebuilt from scratch on every insert, which costs 3.5 ms at + a hundred vectors and four seconds at a hundred thousand. + """ + vector = np.asarray(query, dtype=np.float32).ravel() + query_norm = float(np.linalg.norm(vector)) + if query_norm == 0.0: + return [] + vector = vector / query_norm + exclude_ids = exclude_ids or set() + + matrix, meta = self._matrix(int(vector.size), model) + if not len(matrix): + return [] + + scores = matrix @ vector + if exclude_ids: + keep = np.array([row_id not in exclude_ids for row_id, _, _ in meta]) + scores = np.where(keep, scores, -np.inf) + available = int(keep.sum()) + else: + available = len(scores) + if available == 0: + return [] + + k = min(top_k, available) + # argpartition is linear; a full sort would be O(n log n) for no reason. + candidates = np.argpartition(-scores, k - 1)[:k] + order = candidates[np.argsort(-scores[candidates])] + + return [ + { + "id": meta[i][0], + "text": meta[i][1], + "similarity": float(scores[i]), + "is_question": meta[i][2], + } + for i in order + ] diff --git a/src/mpe_lkg/templates/index.html b/src/mpe_lkg/templates/index.html new file mode 100644 index 0000000..53f3446 --- /dev/null +++ b/src/mpe_lkg/templates/index.html @@ -0,0 +1,267 @@ + + + + + + Local Llama Knowledge Graph + + + + + + +

    Local Llama Knowledge Graph

    +

    This application uses a local Llama model to answer queries, build embeddings, and create a + knowledge graph for exploring related questions and answers.

    + + + + +
    + +
    +
    +
    +
    +
    +
    +
    +
    + + + + diff --git a/templates/index.html b/templates/index.html deleted file mode 100644 index 922b9a9..0000000 --- a/templates/index.html +++ /dev/null @@ -1,248 +0,0 @@ - - - - - - Local Llama Knowledge Graph - - - - - -

    Local Llama Knowledge Graph

    -

    This application uses a local Llama model to answer queries, build embeddings, and create a knowledge graph for exploring related questions and answers.

    - - - - -
    -
    -
    -
    -
    -
    -
    - - - - \ No newline at end of file diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..6900c79 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,57 @@ +import json +import pathlib +import sys + +import pytest + +# Works whether or not the package is installed, and identically on Windows. +ROOT = pathlib.Path(__file__).resolve().parent.parent +sys.path.insert(0, str(ROOT / "src")) +sys.path.insert(0, str(ROOT)) + +from mpe_lkg.backends import DeterministicEmbedding, ScriptedChat # noqa: E402 + + +def step(title: str, content: str, next_action: str = "continue") -> str: + return json.dumps({"title": title, "content": content, "next_action": next_action}) + + +def normal_script(n_steps: int = 6) -> list[str]: + """A well-behaved model: distinct steps, then a final answer.""" + script = [ + step(f"Title {i}", f"Reasoning about part {i} of the problem, considering alternatives.") + for i in range(1, n_steps) + ] + script.append(step("Conclusion", "The capital of France is Paris.", "final_answer")) + return script + + +@pytest.fixture +def embedder(): + return DeterministicEmbedding(dim=48) + + +@pytest.fixture +def flask_client(tmp_path, embedder): + """A Flask test client wired to fakes, with a per-test database.""" + import mpe_lkg.app as app_module + + def make_client(script: list[str], *, repeat_last: bool = False, embed=None): + chat = ScriptedChat(script, repeat_last=repeat_last) + used_embedder = embed or embedder + app_module.app.config["BACKENDS_FACTORY"] = lambda: (chat, used_embedder) + app_module.app.config["DB_PATH"] = str(tmp_path / "test.db") + app_module.app.config["TESTING"] = True + return app_module.app.test_client(), chat + + return make_client + + +def read_events(response) -> list[dict]: + """Parse an SSE response body into event dicts, ignoring heartbeat comments.""" + events = [] + for block in response.get_data(as_text=True).split("\n\n"): + for line in block.splitlines(): + if line.startswith("data: "): + events.append(json.loads(line[6:])) + return events diff --git a/tests/test_graph_math.py b/tests/test_graph_math.py new file mode 100644 index 0000000..6e49095 --- /dev/null +++ b/tests/test_graph_math.py @@ -0,0 +1,160 @@ +"""Invariants of the graph layer. + +Each test here pins something the original implementation got wrong silently. +""" + +import numpy as np +import pytest + +from mpe_lkg.graph import ( + build_graph, + cosine_similarity, + edge_weight_spread, + serialize_graph_data, + strongest_path, + top_similarities, +) + + +def unit(*components) -> np.ndarray: + vector = np.array(components, dtype=np.float32) + return vector / np.linalg.norm(vector) + + +class TestSimilarity: + def test_identical_vectors_score_one(self): + v = unit(1, 2, 3) + assert cosine_similarity(v, v) == pytest.approx(1.0) + + def test_orthogonal_vectors_score_zero(self): + assert cosine_similarity(unit(1, 0), unit(0, 1)) == pytest.approx(0.0, abs=1e-6) + + def test_zero_vector_does_not_produce_nan(self): + assert cosine_similarity(np.zeros(3, dtype=np.float32), unit(1, 1, 1)) == 0.0 + + def test_only_earlier_indices_are_candidates(self): + vectors = np.array([unit(1, 0), unit(0, 1), unit(1, 0.01)], dtype=np.float32) + result = top_similarities(vectors, 2, top_k=2) + assert [index for index, _ in result] == [0, 1] + + def test_first_node_has_no_candidates(self): + vectors = np.array([unit(1, 0), unit(0, 1)], dtype=np.float32) + assert top_similarities(vectors, 0) == [] + + +class TestBuildGraph: + """The node-id to embedding-index mapping is the invariant that used to break.""" + + def test_node_ids_and_vectors_stay_aligned(self): + ids = [f"Step{i}" for i in range(1, 5)] + labels = [f"Step {i}" for i in range(1, 5)] + vectors = np.array([unit(1, 0), unit(0, 1), unit(1, 0.02), unit(0, 1.02)], dtype=np.float32) + + graph = build_graph(ids, labels, vectors, top_k=1) + + # Step3 is nearly identical to Step1, Step4 to Step2. If ids and indices ever + # drift apart, these edges land on the wrong nodes. + by_target = {e["to"]: e["from"] for e in graph["edges"]} + assert by_target["Step3"] == "Step1" + assert by_target["Step4"] == "Step2" + + def test_edges_only_ever_point_at_existing_nodes(self): + ids = [f"Step{i}" for i in range(1, 6)] + vectors = np.random.default_rng(0).standard_normal((5, 8)).astype(np.float32) + graph = build_graph(ids, ids, vectors, top_k=2) + + known = {node["id"] for node in graph["nodes"]} + for edge in graph["edges"]: + assert edge["from"] in known and edge["to"] in known + + def test_mismatched_input_lengths_raise_instead_of_dropping_edges(self): + with pytest.raises(ValueError): + build_graph(["a", "b"], ["a"], np.zeros((2, 4), dtype=np.float32)) + with pytest.raises(ValueError): + build_graph(["a", "b"], ["a", "b"], np.zeros((3, 4), dtype=np.float32)) + + def test_isolated_node_gets_default_size(self): + graph = build_graph(["Step1"], ["Step 1"], np.array([unit(1, 0)])) + assert graph["nodes"][0]["value"] == 20.0 + + +class TestSerialization: + def test_length_survives_serialization(self): + """The spring length was computed and then dropped before reaching vis.js.""" + graph = { + "nodes": [{"id": "Step1", "label": "a"}, {"id": "Step2", "label": "b"}], + "edges": [{"from": "Step1", "to": "Step2", "value": 0.5, "length": 150.0}], + } + edge = serialize_graph_data(graph)["edges"][0] + assert edge["length"] == pytest.approx(150.0) + assert edge["label"] == "0.50" + + def test_length_is_derived_when_absent(self): + graph = {"nodes": [], "edges": [{"from": "a", "to": "b", "value": 0.25}]} + assert serialize_graph_data(graph)["edges"][0]["length"] == pytest.approx(225.0) + + def test_numpy_floats_become_json_safe_floats(self): + graph = {"nodes": [], "edges": [{"from": "a", "to": "b", "value": np.float32(0.5)}]} + assert type(serialize_graph_data(graph)["edges"][0]["value"]) is float + + +class TestStrongestPath: + def test_picks_the_strongest_route_not_the_first_one_found(self): + """A greedy search over negated weights returns whichever path it reaches first.""" + graph = { + "nodes": [{"id": n} for n in ("Step1", "Step2", "Step3", "Step4")], + "edges": [ + # Direct but weak. + {"from": "Step1", "to": "Step4", "value": 0.10}, + # Longer but far stronger: 0.9 * 0.9 * 0.9 = 0.729 > 0.10 + {"from": "Step1", "to": "Step2", "value": 0.90}, + {"from": "Step2", "to": "Step3", "value": 0.90}, + {"from": "Step3", "to": "Step4", "value": 0.90}, + ], + } + path, weights, mean = strongest_path(graph, "Step1", "Step4") + assert path == ["Step1", "Step2", "Step3", "Step4"] + assert weights == pytest.approx([0.9, 0.9, 0.9]) + assert mean == pytest.approx(0.9) + + def test_missing_start_node_returns_none_instead_of_raising(self): + """A graph without Step1 used to raise NetworkXError mid-stream.""" + graph = {"nodes": [{"id": "Step7"}, {"id": "Step8"}], + "edges": [{"from": "Step7", "to": "Step8", "value": 0.5}]} + assert strongest_path(graph, "Step1", "Step8") == (None, None, None) + + def test_defaults_span_first_to_last_node(self): + graph = {"nodes": [{"id": "Step7"}, {"id": "Step8"}], + "edges": [{"from": "Step7", "to": "Step8", "value": 0.5}]} + path, weights, mean = strongest_path(graph) + assert path == ["Step7", "Step8"] + assert mean == pytest.approx(0.5) + + def test_disconnected_graph_returns_none(self): + graph = {"nodes": [{"id": "Step1"}, {"id": "Step2"}], "edges": []} + assert strongest_path(graph) == (None, None, None) + + def test_single_node_graph(self): + assert strongest_path({"nodes": [{"id": "Step1"}], "edges": []}) == (["Step1"], [], 1.0) + + def test_non_positive_similarities_are_not_traversable(self): + """Cosine can go negative; a negative edge is not a strong link.""" + graph = { + "nodes": [{"id": "Step1"}, {"id": "Step2"}], + "edges": [{"from": "Step1", "to": "Step2", "value": -0.4}], + } + assert strongest_path(graph) == (None, None, None) + + def test_empty_graph(self): + assert strongest_path({"nodes": [], "edges": []}) == (None, None, None) + + +class TestEdgeSpread: + def test_reports_coefficient_of_variation(self): + graph = {"edges": [{"value": 0.8}, {"value": 0.8}, {"value": 0.8}]} + spread = edge_weight_spread(graph) + assert spread["n"] == 3 + assert spread["cv"] == pytest.approx(0.0) + + def test_empty_graph_is_not_a_division_by_zero(self): + assert edge_weight_spread({"edges": []})["n"] == 0 diff --git a/tests/test_layers.py b/tests/test_layers.py new file mode 100644 index 0000000..1bcf694 --- /dev/null +++ b/tests/test_layers.py @@ -0,0 +1,249 @@ +"""Reading embeddings from inside a model. + +The addressing and pooling logic is tested against synthetic modules, so it runs +everywhere with no download. The tests that need real weights are marked ``hf`` and +skipped when torch or the model is unavailable. +""" + +import numpy as np +import pytest + +torch = pytest.importorskip("torch") +nn = torch.nn + +from mpe_lkg.layers import ( # noqa: E402 + describe_layers, + find_block_stack, + find_final_norm, + pool, + resolve_layer, +) + + +class Block(nn.Module): + def __init__(self, dim=8): + super().__init__() + self.mlp = nn.Linear(dim, dim) + self.attn = nn.Linear(dim, dim) + + def forward(self, x): + return self.mlp(x) + self.attn(x) + + +class OtherBlock(nn.Module): + def __init__(self, dim=8): + super().__init__() + self.proj = nn.Linear(dim, dim) + + def forward(self, x): + return self.proj(x) + + +class ToyModel(nn.Module): + """Shaped like a decoder: an embedding, a block stack, a final norm.""" + + def __init__(self, n_blocks=6, dim=8): + super().__init__() + self.embed_tokens = nn.Embedding(20, dim) + self.layers = nn.ModuleList([Block(dim) for _ in range(n_blocks)]) + self.norm = nn.LayerNorm(dim) + + def forward(self, x): + h = self.embed_tokens(x) + for block in self.layers: + h = block(h) + return self.norm(h) + + +class TestBlockStackDetection: + def test_finds_the_repeated_stack(self): + name, stack = find_block_stack(ToyModel(n_blocks=6)) + assert name == "layers" + assert len(stack) == 6 + + def test_prefers_the_longest_uniform_stack(self): + class TwoStacks(nn.Module): + def __init__(self): + super().__init__() + self.adapters = nn.ModuleList([OtherBlock() for _ in range(3)]) + self.layers = nn.ModuleList([Block() for _ in range(9)]) + + name, stack = find_block_stack(TwoStacks()) + assert name == "layers" and len(stack) == 9 + + def test_ignores_a_mixed_module_list(self): + class Mixed(nn.Module): + def __init__(self): + super().__init__() + # Longer, but heterogeneous: not a block stack. + self.mixed = nn.ModuleList([Block(), OtherBlock(), Block(), OtherBlock()]) + self.layers = nn.ModuleList([Block() for _ in range(3)]) + + name, _ = find_block_stack(Mixed()) + assert name == "layers" + + def test_a_model_with_no_stack_says_so(self): + class Flat(nn.Module): + def __init__(self): + super().__init__() + self.fc = nn.Linear(8, 8) + + with pytest.raises(ValueError, match="block stack"): + find_block_stack(Flat()) + + def test_describe_layers_lists_what_can_be_addressed(self): + info = describe_layers(ToyModel(n_blocks=4)) + assert info["n_blocks"] == 4 + assert info["block_type"] == "Block" + assert info["addresses"] == ["blocks.0", "blocks.1", "blocks.2", "blocks.3"] + assert set(info["sub_modules"]) == {"mlp", "attn"} + assert info["has_final_norm"] is True + + +class TestLayerAddressing: + def test_positive_index(self): + model = ToyModel(n_blocks=6) + assert resolve_layer(model, "blocks.2") is model.layers[2] + + def test_negative_index_counts_from_the_end(self): + model = ToyModel(n_blocks=6) + assert resolve_layer(model, "blocks.-1") is model.layers[5] + assert resolve_layer(model, "blocks.-2") is model.layers[4] + + def test_sub_module_of_a_block(self): + model = ToyModel(n_blocks=6) + assert resolve_layer(model, "blocks.3.mlp") is model.layers[3].mlp + + def test_explicit_dotted_path_bypasses_detection(self): + model = ToyModel(n_blocks=6) + assert resolve_layer(model, "layers.1.attn") is model.layers[1].attn + + def test_out_of_range_reports_the_available_range(self): + with pytest.raises(IndexError, match="6 blocks"): + resolve_layer(ToyModel(n_blocks=6), "blocks.99") + + def test_non_numeric_index_is_rejected(self): + with pytest.raises(ValueError, match="non-numeric"): + resolve_layer(ToyModel(), "blocks.middle") + + def test_final_norm_is_found(self): + assert isinstance(find_final_norm(ToyModel()), nn.LayerNorm) + + +class TestPooling: + def setup_method(self): + # Two sequences of three tokens; the second is padded to length two. + self.hidden = torch.tensor( + [[[1.0, 0.0], [2.0, 0.0], [3.0, 0.0]], + [[4.0, 0.0], [5.0, 0.0], [0.0, 0.0]]] + ) + self.mask = torch.tensor([[1, 1, 1], [1, 1, 0]]) + + def test_last_token_skips_right_padding(self): + """Pooling the pad instead of the last real token is the classic bug.""" + out = pool(self.hidden, self.mask, "last") + assert out[0, 0].item() == pytest.approx(3.0) + assert out[1, 0].item() == pytest.approx(5.0) + + def test_last_token_handles_left_padding(self): + hidden = torch.tensor([[[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]]]) + mask = torch.tensor([[0, 1, 1]]) + assert pool(hidden, mask, "last")[0, 0].item() == pytest.approx(2.0) + + def test_mean_ignores_padding(self): + out = pool(self.hidden, self.mask, "mean") + assert out[0, 0].item() == pytest.approx(2.0) + assert out[1, 0].item() == pytest.approx(4.5) + + def test_cls_takes_the_first_token(self): + out = pool(self.hidden, self.mask, "cls") + assert out[0, 0].item() == pytest.approx(1.0) + + def test_unknown_pooling_is_rejected(self): + with pytest.raises(ValueError, match="Unknown pooling"): + pool(self.hidden, self.mask, "median") + + +HF_MODEL = "HuggingFaceTB/SmolLM2-135M" + + +@pytest.fixture(scope="module") +def probe(): + pytest.importorskip("transformers") + from mpe_lkg.layers import HiddenStateEmbedding + + try: + return HiddenStateEmbedding(HF_MODEL, layer="blocks.-1") + except Exception as exc: # offline, or the model is not cached + pytest.skip(f"{HF_MODEL} unavailable: {exc}") + + +@pytest.mark.hf +class TestRealModel: + def test_produces_normalised_vectors_of_the_model_width(self, probe): + vectors = probe.embed(["one", "two", "three"]) + assert vectors.shape == (3, probe.dim) + np.testing.assert_allclose(np.linalg.norm(vectors, axis=1), 1.0, atol=1e-5) + + def test_related_text_scores_above_unrelated(self, probe): + v = probe.embed( + ["The capital of France is Paris.", + "Paris is the French capital city.", + "Diesel engine maintenance schedules."] + ) + assert float(v[0] @ v[1]) > float(v[0] @ v[2]) + + def test_the_same_text_always_gives_the_same_vector(self, probe): + a = probe.embed(["deterministic please"]) + b = probe.embed(["deterministic please"]) + np.testing.assert_allclose(a, b, atol=1e-5) + + def test_batching_does_not_shift_a_vector_onto_the_wrong_text(self, probe): + """The invariant that matters at a batch boundary. + + Not elementwise equality: padding changes the shapes the kernels see, so a + batched run differs from a single one in the fourth decimal. What must hold + is that each vector still belongs to its own text -- the failure mode is a + vector sliding onto its neighbour, and that shows up as an off-diagonal + maximum, not as small noise. + """ + texts = [f"sentence number {i}" for i in range(10)] + one_by_one = np.vstack([probe.embed([t]) for t in texts]) + batched = probe.embed(texts) + + agreement = batched @ one_by_one.T + assert np.all(agreement.diagonal() > 0.999), "a vector drifted from its own text" + assert np.array_equal(agreement.argmax(axis=1), np.arange(len(texts))), ( + "a vector matches another text better than its own" + ) + + def test_different_layers_give_different_answers(self, probe): + """If two layers agree exactly, the hook is not tapping where it claims.""" + from mpe_lkg.layers import MultiLayerProbe + + out = MultiLayerProbe(HF_MODEL, ["blocks.0", "blocks.-1"]).embed(["a probe sentence"]) + assert not np.allclose(out["blocks.0"], out["blocks.-1"], atol=1e-3) + + def test_depth_separates_topics_better_than_the_first_layer(self, probe): + """The whole point of the feature, as an assertion.""" + from mpe_lkg.layers import MultiLayerProbe + from scripts.layer_sweep import separation + + texts = ["The capital of France is Paris.", "Paris is the French capital.", + "Diesel engines need oil changes.", "Servicing a diesel engine."] + labels = ["fr", "fr", "eng", "eng"] + + out = MultiLayerProbe(HF_MODEL, ["blocks.0", "blocks.-1"]).embed(texts) + first = separation(out["blocks.0"], labels)["separation"] + last = separation(out["blocks.-1"], labels)["separation"] + assert last > first + + def test_a_module_off_the_forward_path_is_reported(self, probe): + from mpe_lkg.layers import HiddenStateEmbedding + + stray = HiddenStateEmbedding.__new__(HiddenStateEmbedding) + stray.__dict__.update(probe.__dict__) + stray._module = torch.nn.Linear(4, 4) # never called by the model + stray._dim = None + with pytest.raises(RuntimeError, match="never fired"): + stray.embed(["anything"]) diff --git a/tests/test_ollama.py b/tests/test_ollama.py new file mode 100644 index 0000000..6e70209 --- /dev/null +++ b/tests/test_ollama.py @@ -0,0 +1,139 @@ +"""Tests that need a real Ollama. Skipped automatically when it is not there. + +The point of this file is the thing the old code could not do at all: run the same +application against embedding models of different dimensions and have it work. +""" + +import numpy as np +import pytest +from conftest import normal_script, read_events + +from mpe_lkg import backends +from mpe_lkg.backends import OllamaEmbedding + +pytestmark = pytest.mark.ollama + + +def installed() -> set[str]: + return {m["name"] for m in backends.list_models()} + + +def require(model: str) -> str: + names = installed() + if not names: + pytest.skip("Ollama is not reachable") + for name in names: + if name == model or name.split(":")[0] == model.split(":")[0]: + return name + pytest.skip(f"Ollama does not have {model}") + + +@pytest.fixture(scope="module") +def minilm(): + return OllamaEmbedding(require("all-minilm")) + + +class TestRealEmbeddings: + def test_reports_its_own_dimension(self, minilm): + assert minilm.dim == 384 + assert minilm.describe()["dim"] == 384 + + def test_vectors_are_normalised(self, minilm): + vectors = minilm.embed(["the cat sat on the mat", "a dog in the park"]) + assert vectors.shape == (2, 384) + np.testing.assert_allclose(np.linalg.norm(vectors, axis=1), 1.0, atol=1e-5) + + def test_batch_row_count_matches_input_count(self, minilm): + """A silent short return misaligns every vector after the gap.""" + texts = [f"sentence number {i}" for i in range(12)] + assert minilm.embed(texts).shape[0] == len(texts) + + def test_embedded_newlines_do_not_split_a_record(self, minilm): + """One text must produce exactly one vector, whatever is inside it.""" + assert minilm.embed(["first line\nsecond line\n\nthird line"]).shape[0] == 1 + + def test_related_text_scores_above_unrelated_text(self, minilm): + vectors = minilm.embed( + ["The capital of France is Paris.", "Paris is the French capital city.", "Diesel engine maintenance."] + ) + related = float(vectors[0] @ vectors[1]) + unrelated = float(vectors[0] @ vectors[2]) + assert related > unrelated + + def test_missing_model_names_the_pull_command(self): + backend = OllamaEmbedding("definitely-not-a-real-model:v9") + with pytest.raises(backends.BackendError) as excinfo: + backend.embed(["hello"]) + assert "ollama pull" in excinfo.value.hint + + +class TestDimensionIndependence: + """The old similarity search hardcoded 4096 and broke on every other model.""" + + @pytest.mark.parametrize("model,expected_dim", [("all-minilm", 384), ("nomic-embed-text", 768)]) + def test_app_runs_end_to_end(self, flask_client, model, expected_dim): + backend = OllamaEmbedding(require(model)) + if backend.dim != expected_dim: + pytest.skip(f"{model} reported {backend.dim} dimensions, expected {expected_dim}") + + client, _ = flask_client(normal_script(), embed=backend) + events = read_events(client.get("/query?query=What+is+the+capital+of+France")) + + assert not [e for e in events if e["type"] == "error"] + done = [e for e in events if e["type"] == "done"][0] + assert done["embedding"]["dim"] == expected_dim + assert [e for e in events if e["type"] == "similar"][0]["items"] + + def test_switching_model_does_not_corrupt_the_store(self, tmp_path): + """Mixed-dimension rows in one database used to break search silently.""" + from mpe_lkg.store import EmbeddingStore + + store = EmbeddingStore(str(tmp_path / "mixed.db")) + small = OllamaEmbedding(require("all-minilm")) + store.add("small vector text", small.embed(["small vector text"])[0], model=small.model) + store.add("a fake 4096-d row", np.ones(4096, dtype=np.float32), model="legacy") + + hits = store.find_similar(small.embed(["small vector text"])[0], model=small.model) + assert len(hits) == 1 + assert hits[0]["text"] == "small vector text" + store.close() + + +class TestModelDiscovery: + def test_auto_selection_prefers_an_embedding_model(self): + if not any(m["is_embedding"] for m in backends.list_models()): + pytest.skip("no embedding model installed") + backend = OllamaEmbedding("") + assert backend.auto_selected + assert any( + token in backend.model.lower() for token in ("embed", "minilm") + ), f"auto-selected {backend.model}, which is not an embedding model" + + def test_health_is_ok_when_the_chat_model_exists(self, monkeypatch): + names = installed() + if not names: + pytest.skip("Ollama is not reachable") + monkeypatch.setattr(backends, "DEFAULT_CHAT_MODEL", sorted(names)[0]) + assert backends.health()["ok"] is True + + +class TestRealChat: + def test_a_real_model_produces_a_parseable_step(self): + """The response schema is what stops a step from arriving as prose.""" + chat_model = require("llama3.2") + chat = backends.OllamaChat(chat_model) + text = "".join( + chat.stream( + [ + {"role": "system", "content": "Answer in JSON."}, + {"role": "user", "content": "Give one reasoning step about 2+2."}, + ], + 120, + schema=backends.STEP_SCHEMA, + ) + ) + from mpe_lkg.reasoning import extract_json + + parsed = extract_json(text) + assert set(parsed) >= {"title", "content", "next_action"} + assert parsed["next_action"] in ("continue", "final_answer") diff --git a/tests/test_render.py b/tests/test_render.py new file mode 100644 index 0000000..1453c60 --- /dev/null +++ b/tests/test_render.py @@ -0,0 +1,199 @@ +"""Does the graph actually get drawn? + +Everything else in this suite asserts on the data sent to the browser. This file +runs a real browser against a real server and reads the state back out of vis.js, so +a change that serialises perfectly but never renders is still caught. + +Vendored copies of vis-network and marked are used, so these tests do not depend on +a CDN being reachable or on upstream not changing under us. +""" + +import socket +import threading + +import pytest +from conftest import normal_script, step +from werkzeug.serving import make_server + +from mpe_lkg.backends import DeterministicEmbedding, ScriptedChat + +pytest.importorskip("playwright.sync_api") +from playwright.sync_api import sync_playwright # noqa: E402 + + +def free_port() -> int: + with socket.socket() as sock: + sock.bind(("127.0.0.1", 0)) + return int(sock.getsockname()[1]) + + +class LiveServer: + def __init__(self, script, tmp_path, *, repeat_last=False, dim=48, delay=0.0): + import mpe_lkg.app as app_module + + chat = ScriptedChat(script, repeat_last=repeat_last, delay=delay) + app_module.app.config["BACKENDS_FACTORY"] = lambda: (chat, DeterministicEmbedding(dim)) + app_module.app.config["DB_PATH"] = str(tmp_path / "render.db") + self.port = free_port() + self._server = make_server("127.0.0.1", self.port, app_module.app, threaded=True) + self._thread = threading.Thread(target=self._server.serve_forever, daemon=True) + + @property + def url(self) -> str: + return f"http://127.0.0.1:{self.port}" + + def __enter__(self): + self._thread.start() + return self + + def __exit__(self, *exc): + self._server.shutdown() + self._thread.join(timeout=5) + + +@pytest.fixture(scope="session") +def browser(): + with sync_playwright() as playwright: + instance = playwright.chromium.launch() + yield instance + instance.close() + + +@pytest.fixture +def page(browser): + context = browser.new_context(viewport={"width": 1280, "height": 900}) + page = context.new_page() + errors = [] + page.on("pageerror", lambda exc: errors.append(str(exc))) + page.on("console", lambda msg: errors.append(msg.text) if msg.type == "error" else None) + yield page + context.close() + assert not errors, f"browser reported errors: {errors}" + + +def run_query(page, server, text="What is the capital of France?"): + page.goto(server.url) + page.fill("#query", text) + page.click("#submit") + page.wait_for_function("() => document.querySelector('#submit').disabled === false", timeout=30_000) + + +def graph_state(page) -> dict: + return page.evaluate("() => ({nodes: nodes.get(), edges: edges.get()})") + + +class TestRendering: + def test_nodes_and_edges_reach_visjs(self, page, tmp_path): + with LiveServer(normal_script(), tmp_path) as server: + run_query(page, server) + state = graph_state(page) + + assert len(state["nodes"]) == 6 + assert state["edges"], "similarity edges must be drawn" + known = {n["id"] for n in state["nodes"]} + for edge in state["edges"]: + assert edge["from"] in known and edge["to"] in known + + def test_edges_carry_a_similarity_label_and_length(self, page, tmp_path): + with LiveServer(normal_script(), tmp_path) as server: + run_query(page, server) + state = graph_state(page) + + for edge in state["edges"]: + assert edge["label"], "the similarity value is drawn on the edge" + assert edge["length"] > 0, "the spring length must reach vis.js" + + def test_the_graph_paints_pixels(self, page, tmp_path): + """A canvas that stays blank would pass every data-level assertion.""" + with LiveServer(normal_script(), tmp_path) as server: + run_query(page, server) + page.wait_for_timeout(1200) # let the physics settle + painted = page.evaluate( + """() => { + const c = document.querySelector('#graph canvas'); + const ctx = c.getContext('2d'); + const {data} = ctx.getImageData(0, 0, c.width, c.height); + let n = 0; + for (let i = 3; i < data.length; i += 4) if (data[i] > 0) n++; + return n; + }""" + ) + assert painted > 1000, "the graph canvas is essentially empty" + + def test_steps_and_final_answer_are_shown(self, page, tmp_path): + with LiveServer(normal_script(), tmp_path) as server: + run_query(page, server) + assert page.locator(".step").count() == 5 + assert page.locator(".final-answer").count() == 1 + assert "Paris" in page.locator(".final-answer").inner_text() + + def test_screenshot_artifact(self, page, tmp_path): + with LiveServer(normal_script(), tmp_path) as server: + run_query(page, server) + page.wait_for_timeout(1200) + out = tmp_path / "graph.png" + page.screenshot(path=str(out), full_page=True) + assert out.stat().st_size > 10_000 + + +class TestErrorsAreVisible: + def test_backend_failure_is_shown_on_the_page(self, page, tmp_path): + """The reported symptom was a blank page with the error only in the console.""" + with LiveServer([], tmp_path) as server: # a chat backend with nothing to say + run_query(page, server) + assert page.locator(".error-box").count() >= 1 + assert page.locator(".error-box").first.inner_text().strip() + + def test_submit_is_re_enabled_after_an_error(self, page, tmp_path): + with LiveServer([], tmp_path) as server: + run_query(page, server) + assert page.locator("#submit").is_enabled() + + +class TestInteraction: + def test_enter_key_submits(self, page, tmp_path): + """There was no keydown handler at all; pressing Enter did nothing.""" + with LiveServer(normal_script(), tmp_path) as server: + page.goto(server.url) + page.fill("#query", "capital of France") + page.press("#query", "Enter") + page.wait_for_selector(".step", timeout=30_000) + page.wait_for_function("() => !document.querySelector('#submit').disabled", timeout=30_000) + assert page.locator(".step").count() >= 1 + + def test_submit_is_disabled_while_streaming(self, page, tmp_path): + # A backend that answers instantly makes the in-flight state unobservable, + # so the fake is slowed down enough for the browser to be caught mid-run. + with LiveServer(normal_script(), tmp_path, delay=0.4) as server: + page.goto(server.url) + page.fill("#query", "q") + page.click("#submit") + page.wait_for_function("() => document.querySelector('#submit').disabled === true", timeout=5_000) + page.wait_for_function("() => !document.querySelector('#submit').disabled", timeout=30_000) + + def test_second_run_replaces_the_first(self, page, tmp_path): + """The old page never closed the previous EventSource, so runs interleaved.""" + with LiveServer(normal_script() + normal_script(), tmp_path) as server: + run_query(page, server, "first question") + run_query(page, server, "second question") + assert page.locator(".step").count() == 5 + assert page.evaluate("() => eventSource === null") + + def test_png_export_produces_a_file(self, page, tmp_path): + with LiveServer(normal_script(), tmp_path) as server: + run_query(page, server) + page.wait_for_timeout(1000) + with page.expect_download(timeout=15_000) as download: + page.click("#download-img") + saved = tmp_path / "export.png" + download.value.save_as(str(saved)) + + assert saved.stat().st_size > 5_000 + assert saved.read_bytes()[:8] == b"\x89PNG\r\n\x1a\n" + + +class TestTruncationNotice: + def test_shortened_step_is_marked_in_the_ui(self, page, tmp_path): + with LiveServer([step("Long", "y" * 900)], tmp_path, repeat_last=True) as server: + run_query(page, server) + assert page.locator(".notice").count() >= 1 diff --git a/tests/test_store.py b/tests/test_store.py new file mode 100644 index 0000000..ec5fe57 --- /dev/null +++ b/tests/test_store.py @@ -0,0 +1,156 @@ +"""The embedding store: exactness, growth across questions, and mixed models. + +Exactness is the point of these. The index this project used to depend on returns +the wrong neighbour on a current numpy (see scripts/bench_search.py and the +README), so "the search is exact" is now a property with a test behind it rather +than an assumption. +""" + +import numpy as np +import pytest +from conftest import normal_script, read_events + +from mpe_lkg.store import EmbeddingStore + + +def unit(rng, dim=32): + v = rng.standard_normal(dim).astype(np.float32) + return v / np.linalg.norm(v) + + +@pytest.fixture +def store(tmp_path): + s = EmbeddingStore(str(tmp_path / "s.db")) + yield s + s.close() + + +class TestExactness: + def test_a_vector_is_its_own_nearest_neighbour(self, store): + """The failure mode that made the old index useless.""" + rng = np.random.default_rng(0) + vectors = [unit(rng) for _ in range(50)] + for i, v in enumerate(vectors): + store.add(f"text {i}", v, model="m") + + for i in (0, 7, 23, 49): + hits = store.find_similar(vectors[i], top_k=1, model="m") + assert hits[0]["text"] == f"text {i}" + assert hits[0]["similarity"] == pytest.approx(1.0, abs=1e-5) + + def test_ranking_matches_a_full_brute_force_sort(self, store): + rng = np.random.default_rng(1) + vectors = [unit(rng) for _ in range(200)] + for i, v in enumerate(vectors): + store.add(f"text {i}", v, model="m") + + query = unit(rng) + matrix = np.vstack(vectors) + expected = np.argsort(-(matrix @ query))[:5] + + hits = store.find_similar(query, top_k=5, model="m") + assert [h["text"] for h in hits] == [f"text {i}" for i in expected] + # Scores must be monotonically decreasing. + assert all(a["similarity"] >= b["similarity"] for a, b in zip(hits, hits[1:], strict=False)) + + def test_requesting_more_than_exists_is_not_an_error(self, store): + rng = np.random.default_rng(2) + store.add("only one", unit(rng), model="m") + assert len(store.find_similar(unit(rng), top_k=10, model="m")) == 1 + + def test_excluded_ids_never_appear(self, store): + rng = np.random.default_rng(3) + target = unit(rng) + row_id = store.add("the query itself", target, model="m") + for i in range(20): + store.add(f"other {i}", unit(rng), model="m") + + hits = store.find_similar(target, top_k=5, model="m", exclude_ids={row_id}) + assert all(h["id"] != row_id for h in hits) + assert len(hits) == 5 + + def test_excluding_everything_returns_nothing(self, store): + rng = np.random.default_rng(4) + ids = {store.add(f"t{i}", unit(rng), model="m") for i in range(3)} + assert store.find_similar(unit(rng), model="m", exclude_ids=ids) == [] + + def test_empty_store(self, store): + assert store.find_similar(np.ones(32, dtype=np.float32), model="m") == [] + + +class TestCacheInvalidation: + def test_a_new_row_is_visible_to_the_next_search(self, store): + """The in-memory matrix must not outlive a write.""" + rng = np.random.default_rng(5) + store.add("first", unit(rng), model="m") + target = unit(rng) + store.find_similar(target, model="m") # warms the cache + + store.add("second", target, model="m") + hits = store.find_similar(target, top_k=1, model="m") + assert hits[0]["text"] == "second" + + def test_clear_empties_the_cache_too(self, store): + rng = np.random.default_rng(6) + v = unit(rng) + store.add("gone", v, model="m") + store.find_similar(v, model="m") + store.clear() + assert store.find_similar(v, model="m") == [] + assert store.count() == 0 + + +class TestMixedModels: + def test_dimensions_never_mix(self, store): + rng = np.random.default_rng(7) + small = unit(rng, 32) + store.add("384-ish row", small, model="small") + store.add("legacy 4096 row", np.ones(4096, dtype=np.float32), model="legacy") + + hits = store.find_similar(small, model="small") + assert [h["text"] for h in hits] == ["384-ish row"] + + def test_same_dimension_different_model_is_kept_apart(self, store): + rng = np.random.default_rng(8) + v = unit(rng, 32) + store.add("from model A", v, model="A") + store.add("from model B", v, model="B") + + assert [h["text"] for h in store.find_similar(v, model="A")] == ["from model A"] + assert [h["text"] for h in store.find_similar(v, model="B")] == ["from model B"] + + +class TestGrowthAcrossQuestions: + """The store accumulates now; it used to be wiped at the start of each request.""" + + def test_a_later_question_can_see_an_earlier_one(self, flask_client): + client, _ = flask_client(normal_script() + normal_script()) + + read_events(client.get("/query?query=What+is+the+capital+of+France")) + events = read_events(client.get("/query?query=Tell+me+about+French+cities")) + + similar = [e for e in events if e["type"] == "similar"][0] + assert similar["store_size"] > 6, "the second run must still see the first run's rows" + assert similar["items"] + + def test_store_size_grows_monotonically(self, flask_client): + client, _ = flask_client(normal_script() * 3) + + sizes = [] + for i in range(3): + events = read_events(client.get(f"/query?query=question+{i}")) + sizes.append([e for e in events if e["type"] == "similar"][0]["store_size"]) + + assert sizes == sorted(sizes) and len(set(sizes)) == 3 + + def test_hundreds_of_rows_stay_fast_and_exact(self, store): + """The scale actually in question: hundreds of rows across many questions.""" + rng = np.random.default_rng(9) + vectors = [unit(rng, 384) for _ in range(500)] + for i, v in enumerate(vectors): + store.add(f"row {i}", v, model="m") + + assert store.count() == 500 + hits = store.find_similar(vectors[321], top_k=3, model="m") + assert hits[0]["text"] == "row 321" + assert hits[0]["similarity"] == pytest.approx(1.0, abs=1e-5) diff --git a/tests/test_stream.py b/tests/test_stream.py new file mode 100644 index 0000000..321519e --- /dev/null +++ b/tests/test_stream.py @@ -0,0 +1,218 @@ +"""End-to-end behaviour of the /query stream, with no model and no network. + +The two reported issues were both "the page stays blank". These tests pin the two +distinct causes: an exception thrown before the stream opened, and a retry loop that +never yielded anything. +""" + +import json + +import pytest +from conftest import normal_script, read_events, step + +from mpe_lkg.backends import BackendError, DeterministicEmbedding + + +class FailingEmbedding: + """Stands in for Ollama answering 404 because the model is not installed.""" + + def __init__(self, message="Ollama does not have the embedding model 'llama3.1:8b'."): + self._message = message + + @property + def dim(self): + return 8 + + def describe(self): + return {"kind": "failing", "model": "llama3.1:8b", "dim": 8} + + def embed(self, texts): + raise BackendError(self._message, hint="Install it with: ollama pull llama3.1:8b") + + +class TestHappyPath: + def test_stream_reaches_a_final_answer(self, flask_client): + client, _ = flask_client(normal_script()) + events = read_events(client.get("/query?query=capital+of+France")) + kinds = [e["type"] for e in events] + + assert "step" in kinds + assert kinds.count("final") == 1 + assert kinds.count("done") == 1 + assert kinds[-1] == "done_stream" + assert "error" not in kinds + + def test_every_step_carries_a_drawable_graph(self, flask_client): + client, _ = flask_client(normal_script()) + events = read_events(client.get("/query?query=q")) + + for event in [e for e in events if e["type"] in ("step", "final")]: + graph = event["graph"] + known = {n["id"] for n in graph["nodes"]} + assert known, "a step must always produce at least one node" + for edge in graph["edges"]: + assert edge["from"] in known and edge["to"] in known + assert "length" in edge + + def test_node_count_grows_by_one_per_step(self, flask_client): + client, _ = flask_client(normal_script()) + events = read_events(client.get("/query?query=q")) + counts = [len(e["graph"]["nodes"]) for e in events if e["type"] == "step"] + assert counts == list(range(1, len(counts) + 1)) + + def test_final_answer_does_not_duplicate_the_last_step(self, flask_client): + """Two nodes over identical text produce a spurious 1.00 edge between them.""" + client, _ = flask_client(normal_script()) + events = read_events(client.get("/query?query=q")) + final = [e for e in events if e["type"] == "final"][0] + + labels = [n["label"] for n in final["graph"]["nodes"]] + assert sum(1 for label in labels if label.startswith("Final Answer")) == 1 + assert all(edge["value"] < 0.999 for edge in final["graph"]["edges"]) + + def test_related_items_are_reported_with_named_fields(self, flask_client): + client, _ = flask_client(normal_script()) + events = read_events(client.get("/query?query=q")) + similar = [e for e in events if e["type"] == "similar"] + assert similar + for item in similar[0]["items"]: + assert set(item) == {"id", "text", "similarity", "is_question"} + assert -1.0 <= item["similarity"] <= 1.0 + + +class TestIssueOneBlankPage: + """An embedding failure must arrive as an event, not as an HTTP 500.""" + + def test_embedding_failure_yields_an_error_event(self, flask_client): + client, _ = flask_client(normal_script(), embed=FailingEmbedding()) + response = client.get("/query?query=q") + + assert response.status_code == 200 + events = read_events(response) + errors = [e for e in events if e["type"] == "error"] + assert errors, "the browser must be told why nothing happened" + assert "ollama pull" in errors[0]["hint"] + + def test_error_stream_still_terminates(self, flask_client): + client, _ = flask_client(normal_script(), embed=FailingEmbedding()) + events = read_events(client.get("/query?query=q")) + assert events[-1]["type"] == "done_stream" + + def test_chat_failure_yields_an_error_event(self, flask_client): + client, _ = flask_client([]) # ScriptedChat with nothing to say + events = read_events(client.get("/query?query=q")) + assert [e for e in events if e["type"] == "error"] + + def test_empty_query_is_rejected_clearly(self, flask_client): + client, _ = flask_client(normal_script()) + assert client.get("/query?query=").status_code == 400 + assert client.get("/query?query=%20%20").status_code == 400 + + +class TestIssueTwoNeverTerminates: + """Both retry branches used to loop without advancing the step counter.""" + + def test_endlessly_long_answers_still_terminate(self, flask_client): + long_step = step("Long", "x" * 900) + client, _ = flask_client([long_step], repeat_last=True) + + events = read_events(client.get("/query?query=q")) + + assert events[-1]["type"] == "done_stream" + assert [e for e in events if e["type"] == "final"] + steps = [e for e in events if e["type"] == "step"] + assert steps and all(e["truncated"] for e in steps) + assert all(len(e["content"]) <= 704 for e in steps) + + def test_model_that_always_wants_to_stop_still_terminates(self, flask_client): + finish = step("Done", "Answering immediately.", "final_answer") + client, _ = flask_client([finish], repeat_last=True) + + events = read_events(client.get("/query?query=q")) + + assert events[-1]["type"] == "done_stream" + # It is nudged up to the minimum before being allowed to finish. The last + # node is the final answer, so it is not also announced as a step. + final = [e for e in events if e["type"] == "final"][0] + assert len(final["graph"]["nodes"]) >= 5 + + def test_step_count_is_bounded(self, flask_client): + never_finish = step("Go on", "Still reasoning about the problem.") + client, _ = flask_client([never_finish], repeat_last=True) + + events = read_events(client.get("/query?query=q")) + assert len([e for e in events if e["type"] == "step"]) <= 20 + assert events[-1]["type"] == "done_stream" + + +class TestMalformedModelOutput: + def test_non_json_answer_becomes_a_visible_step(self, flask_client): + """It used to become a node literally labelled 'Parsing Error'.""" + client, _ = flask_client( + ["I refuse to answer in JSON.", *normal_script()], + ) + events = read_events(client.get("/query?query=q")) + first = [e for e in events if e["type"] == "step"][0] + + assert "Parsing Error" not in first["title"] + assert first["content"] == "I refuse to answer in JSON." + + def test_json_wrapped_in_a_code_fence_is_understood(self, flask_client): + fenced = "```json\n" + step("Fenced", "Content inside a fence.") + "\n```" + client, _ = flask_client([fenced, *normal_script()]) + events = read_events(client.get("/query?query=q")) + first = [e for e in events if e["type"] == "step"][0] + assert first["title"] == "Fenced" + + def test_apostrophes_are_preserved(self, flask_client): + """The old streamer stripped every ' from the model's text.""" + client, _ = flask_client([step("T", "It doesn't drop the model's apostrophes."), + *normal_script()]) + events = read_events(client.get("/query?query=q")) + assert "doesn't" in [e for e in events if e["type"] == "step"][0]["content"] + + +class TestHealthRoute: + def test_health_reports_a_problem_when_ollama_is_absent(self, flask_client, monkeypatch): + from mpe_lkg import backends + + monkeypatch.setattr(backends, "list_models", lambda *a, **k: []) + client, _ = flask_client(normal_script()) + payload = client.get("/health").get_json() + + assert payload["ok"] is False + assert "ollama serve" in payload["hint"] + + def test_health_names_the_missing_model(self, flask_client, monkeypatch): + from mpe_lkg import backends + + monkeypatch.setattr( + backends, "list_models", + lambda *a, **k: [{"name": "all-minilm:latest", "is_embedding": True, "capabilities": []}], + ) + client, _ = flask_client(normal_script()) + payload = client.get("/health").get_json() + + assert payload["ok"] is False + assert "ollama pull" in payload["hint"] + assert "all-minilm:latest" in payload["models"] + + +class TestEmbeddingDimensions: + @pytest.mark.parametrize("dim", [8, 48, 384, 768, 4096]) + def test_any_embedding_size_works(self, flask_client, dim): + """The old similarity search hardcoded 4096 and broke on anything else.""" + client, _ = flask_client(normal_script(), embed=DeterministicEmbedding(dim=dim)) + events = read_events(client.get("/query?query=q")) + + assert [e for e in events if e["type"] == "final"] + assert not [e for e in events if e["type"] == "error"] + done = [e for e in events if e["type"] == "done"][0] + assert done["embedding"]["dim"] == dim + + def test_events_are_json_serialisable_end_to_end(self, flask_client): + client, _ = flask_client(normal_script()) + raw = client.get("/query?query=q").get_data(as_text=True) + for line in raw.splitlines(): + if line.startswith("data: "): + json.loads(line[6:])