commit 03075f1ef10df1c14b51ee276c955a9f24c96c81 Author: grigo Date: Mon May 4 07:56:45 2026 +0300 Initial import: WebAisMap Closes TG-4 Co-authored-by: Cursor diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5180a0e --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +__pycache__/ +*.py[cod] +.venv/ +venv/ +*.pem diff --git a/README.md b/README.md new file mode 100644 index 0000000..12de7b3 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# WebAisMap + +AIS map web application (Python backend + Leaflet frontend). diff --git a/README_OFFLINE.md b/README_OFFLINE.md new file mode 100644 index 0000000..3f9ca03 --- /dev/null +++ b/README_OFFLINE.md @@ -0,0 +1,97 @@ +# Работа в офлайн режиме + +Приложение настроено для работы без подключения к интернету. Все необходимые файлы (библиотека Leaflet и тайлы карты) хранятся локально. + +## Структура файлов + +- `static/leaflet/` - библиотека Leaflet (CSS, JS и изображения) +- `static/xterm/` - терминал в браузере (вкладка «Консоль»), без CDN + (не удаляйте комментарий `sourceMappingURL` через `splitlines()` в Python — в `xterm.min.js` есть символ U+0085, из‑за него строка «ломается» и появляется SyntaxError в браузере) +- `static/tiles/` - тайлы карты OpenStreetMap + +## Скачивание тайлов карты + +Для работы в офлайн режиме необходимо предварительно скачать тайлы карты для нужной области. + +### Использование скрипта download_tiles.py + +```bash +# Скачать тайлы для области Москвы (по умолчанию) +python download_tiles.py + +# Скачать тайлы для конкретной области +python download_tiles.py --bounds 55.5,37.5,56.0,38.0 --min-zoom 5 --max-zoom 12 + +# Скачать тайлы вокруг центра с радиусом +python download_tiles.py --center 55.75,37.62,50 --min-zoom 5 --max-zoom 12 + +# Указать выходную папку +python download_tiles.py --output static/tiles --min-zoom 5 --max-zoom 12 +``` + +### Параметры скрипта + +- `--min-zoom` - минимальный уровень масштабирования (по умолчанию: 5) +- `--max-zoom` - максимальный уровень масштабирования (по умолчанию: 12) +- `--bounds` - границы области в формате: `min_lat,min_lon,max_lat,max_lon` +- `--center` - центр области и радиус в формате: `lat,lon,radius_km` +- `--output` - выходная папка для тайлов (по умолчанию: `static/tiles`) +- `--delay` - задержка между запросами в секундах (по умолчанию: 1.0, минимум рекомендуется 1.0) + +### Примеры использования + +```bash +# Москва и окрестности, уровни 5-12 +python download_tiles.py --bounds 55.5,37.3,56.0,37.9 --min-zoom 5 --max-zoom 12 + +# Санкт-Петербург, уровни 5-14 +python download_tiles.py --center 59.934,30.306,30 --min-zoom 5 --max-zoom 14 + +# Большая область (например, весь регион), уровни 3-10 +python download_tiles.py --bounds 50.0,20.0,60.0,40.0 --min-zoom 3 --max-zoom 10 +``` + +### Рекомендации по уровням масштабирования + +- **Уровни 0-5**: Весь мир / континенты (очень мало тайлов, быстро скачивается) +- **Уровни 5-10**: Регионы / крупные города (умеренное количество тайлов) +- **Уровни 10-14**: Города / районы (много тайлов, может занять время) +- **Уровни 14-19**: Детальные карты (очень много тайлов, долгое скачивание) + +**Важно**: Чем больше уровней масштабирования и область, тем больше тайлов нужно скачать. Для начала рекомендуется использовать уровни 5-12 для нужной области. + +## Запуск приложения + +После скачивания тайлов приложение будет работать полностью офлайн: + +```bash +python main.py +``` + +Приложение будет доступно по адресу `http://localhost:8000` + +## Примечания + +- Если тайл не найден локально, карта покажет пустое место (серый квадрат) +- Для полного покрытия области рекомендуется скачать тайлы заранее +- Размер тайлов зависит от области и уровней масштабирования (может быть от нескольких МБ до нескольких ГБ) + +## Политика использования тайлов OpenStreetMap + +Скрипт настроен для соблюдения [политики использования тайлов OpenStreetMap](https://operations.osmfoundation.org/policies/tiles/): + +- ✅ Используется корректный User-Agent в заголовках запросов +- ✅ Соблюдается минимальная задержка 1 секунда между запросами +- ✅ Обрабатываются ошибки 429 (Too Many Requests) с автоматическим увеличением задержки +- ✅ Используются разные поддомены для распределения нагрузки + +**Важно**: Если вы получаете ошибку "Access blocked", убедитесь, что: +1. Используется задержка не менее 1 секунды (`--delay 1.0` или больше) +2. Не запускаете несколько экземпляров скрипта одновременно +3. Не скачиваете слишком много тайлов за короткое время + +Для больших областей рекомендуется: +- Использовать задержку 1.5-2 секунды: `--delay 1.5` +- Скачивать тайлы в несколько этапов (разные уровни масштабирования отдельно) +- Делать перерывы между сессиями скачивания + diff --git a/SVG/ChosenTarget.svg b/SVG/ChosenTarget.svg new file mode 100644 index 0000000..ef8eb0c --- /dev/null +++ b/SVG/ChosenTarget.svg @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SVG/Engine.svg b/SVG/Engine.svg new file mode 100644 index 0000000..ce39c98 --- /dev/null +++ b/SVG/Engine.svg @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SVG/LosingTarget.svg b/SVG/LosingTarget.svg new file mode 100644 index 0000000..c1f21f1 --- /dev/null +++ b/SVG/LosingTarget.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/SVG/Moored.svg b/SVG/Moored.svg new file mode 100644 index 0000000..df83abd --- /dev/null +++ b/SVG/Moored.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/SVG/SVG/Engine.svg b/SVG/SVG/Engine.svg new file mode 100644 index 0000000..ce39c98 --- /dev/null +++ b/SVG/SVG/Engine.svg @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SVG/SVG/Moored.svg b/SVG/SVG/Moored.svg new file mode 100644 index 0000000..df83abd --- /dev/null +++ b/SVG/SVG/Moored.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/SVG/SVG/achor.svg b/SVG/SVG/achor.svg new file mode 100644 index 0000000..064fdfe --- /dev/null +++ b/SVG/SVG/achor.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/SVG/SVG/cog.svg b/SVG/SVG/cog.svg new file mode 100644 index 0000000..a321443 --- /dev/null +++ b/SVG/SVG/cog.svg @@ -0,0 +1,16 @@ + + + + + + + + + \ No newline at end of file diff --git a/SVG/SVG/cursor.svg b/SVG/SVG/cursor.svg new file mode 100644 index 0000000..e863fe8 --- /dev/null +++ b/SVG/SVG/cursor.svg @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SVG/SVG/fishing.svg b/SVG/SVG/fishing.svg new file mode 100644 index 0000000..e6351e5 --- /dev/null +++ b/SVG/SVG/fishing.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/SVG/SVG/fishing_1.svg b/SVG/SVG/fishing_1.svg new file mode 100644 index 0000000..047879f --- /dev/null +++ b/SVG/SVG/fishing_1.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SVG/SVG/ownShip.svg b/SVG/SVG/ownShip.svg new file mode 100644 index 0000000..407f0f3 --- /dev/null +++ b/SVG/SVG/ownShip.svg @@ -0,0 +1,16 @@ + + + + + + + + + \ No newline at end of file diff --git a/SVG/SVG/sail.svg b/SVG/SVG/sail.svg new file mode 100644 index 0000000..8f6b682 --- /dev/null +++ b/SVG/SVG/sail.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/SVG/SVG/Ресурс 8.svg b/SVG/SVG/Ресурс 8.svg new file mode 100644 index 0000000..064fdfe --- /dev/null +++ b/SVG/SVG/Ресурс 8.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/SVG/ScaleTarget.svg b/SVG/ScaleTarget.svg new file mode 100644 index 0000000..17fdfc0 --- /dev/null +++ b/SVG/ScaleTarget.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/SVG/Target.svg b/SVG/Target.svg new file mode 100644 index 0000000..3a3b326 --- /dev/null +++ b/SVG/Target.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/SVG/TargetClassA.svg b/SVG/TargetClassA.svg new file mode 100644 index 0000000..9694880 --- /dev/null +++ b/SVG/TargetClassA.svg @@ -0,0 +1,16 @@ + + + + + + + + + \ No newline at end of file diff --git a/SVG/achor.svg b/SVG/achor.svg new file mode 100644 index 0000000..064fdfe --- /dev/null +++ b/SVG/achor.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/SVG/base_station.svg b/SVG/base_station.svg new file mode 100644 index 0000000..8054301 --- /dev/null +++ b/SVG/base_station.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SVG/buey.svg b/SVG/buey.svg new file mode 100644 index 0000000..9c39e35 --- /dev/null +++ b/SVG/buey.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/SVG/compass.svg b/SVG/compass.svg new file mode 100644 index 0000000..d24b894 --- /dev/null +++ b/SVG/compass.svg @@ -0,0 +1,24 @@ + + + + + + + + + + \ No newline at end of file diff --git a/SVG/fishing.svg b/SVG/fishing.svg new file mode 100644 index 0000000..047879f --- /dev/null +++ b/SVG/fishing.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SVG/icon.svg b/SVG/icon.svg new file mode 100644 index 0000000..4cea200 --- /dev/null +++ b/SVG/icon.svg @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SVG/sail.svg b/SVG/sail.svg new file mode 100644 index 0000000..8f6b682 --- /dev/null +++ b/SVG/sail.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/SVG/targetList.svg b/SVG/targetList.svg new file mode 100644 index 0000000..c1cc506 --- /dev/null +++ b/SVG/targetList.svg @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + MMSI + COG + SOG + ... + + \ No newline at end of file diff --git a/SVG/targetList_1.svg b/SVG/targetList_1.svg new file mode 100644 index 0000000..23481f3 --- /dev/null +++ b/SVG/targetList_1.svg @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ais_nrzi_pipeline/__init__.py b/ais_nrzi_pipeline/__init__.py new file mode 100644 index 0000000..978d1b2 --- /dev/null +++ b/ais_nrzi_pipeline/__init__.py @@ -0,0 +1 @@ +# gr-aistx-compatible PHY (phy.py) + optional encode_to_nrzi CLI diff --git a/ais_nrzi_pipeline/encode_to_nrzi.py b/ais_nrzi_pipeline/encode_to_nrzi.py new file mode 100644 index 0000000..f5bc8e9 --- /dev/null +++ b/ais_nrzi_pipeline/encode_to_nrzi.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +CLI: AIS fields (type, MMSI, lat/lon, …) -> NRZI bit stream (and optional packed bytes). + +Uses AIVDM_Encoder.py in the parent directory for the PDU, then phy.build_nrzi_frame +(same stages as gr-aistx Build_Frame: CRC, reverse, stuff, flags, NRZI). +""" + +from __future__ import annotations + +import argparse +import os +import sys + +# Каталог со скриптом (phy.py) и корень репозитория (опционально AIVDM_Encoder.py) +_SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__)) +_ROOT = os.path.abspath(os.path.join(_SCRIPT_DIR, "..")) +for _p in (_SCRIPT_DIR, _ROOT): + if _p not in sys.path: + sys.path.insert(0, _p) + +from phy import build_nrzi_frame, nrzi_bits_to_bytes # noqa: E402 + +try: + import AIVDM_Encoder as enc # noqa: E402 +except ImportError: + enc = None # type: ignore + + +def build_payload(options) -> str: + if enc is None: + raise SystemExit( + "AIVDM_Encoder.py не найден в корне репозитория. " + "Для веб-транспондера используйте pyais + опцию «Кодер: phy.py»." + ) + t = options.type + if t == 1: + return enc.encode_1( + int(options.mmsi), int(options.status), float(options.speed), + float(options.lon), float(options.lat), float(options.course), int(options.ts), + ) + if t == 4: + return enc.encode_4( + int(options.mmsi), float(options.speed), + float(options.lon), float(options.lat), float(options.course), int(options.ts), + ) + if t == 14: + return enc.encode_14(int(options.mmsi), options.sart_msg) + if t == 18: + return enc.encode_18( + int(options.mmsi), float(options.speed), + float(options.lon), float(options.lat), float(options.course), int(options.ts), + ) + if t == 20: + return enc.encode_20( + int(options.mmsi), int(options.fatdmaoffset), int(options.fatdmaslots), + int(options.fatdmatimeout), int(options.fatdmaincrement), + ) + if t == 21: + v = 1 if options.v_AtoN else 0 + return enc.encode_21( + int(options.mmsi), int(options.aid_type), options.aid_name, + float(options.lon), float(options.lat), options.vsize, v, + ) + if t == 22: + return enc.encode_22( + int(options.mmsi), int(options.channel_a), int(options.channel_b), + float(options.ne_lon), float(options.ne_lat), float(options.sw_lon), float(options.sw_lat), + ) + if t == 23: + return enc.encode_23( + int(options.mmsi), float(options.ne_lon), float(options.ne_lat), + float(options.sw_lon), float(options.sw_lat), int(options.interval), int(options.quiet), + ) + if t == 24: + if options.part.upper() == "A": + return enc.encode_24(int(options.mmsi), "A", __vname=options.vname.upper()) + return enc.encode_24( + int(options.mmsi), "B", + __callsign=options.callsign.upper(), __vsize=options.vsize, __vtype=int(options.vtype), + ) + raise SystemExit("Unsupported type %r" % (t,)) + + +def main() -> None: + p = argparse.ArgumentParser(description="AIS parameters -> NRZI bit frame (gr-aistx-compatible chain).") + p.add_argument("--type", type=int, required=True, help="AIS message type (1,4,14,18,20-24)") + p.add_argument("--mmsi", type=int, default=247320162) + p.add_argument("--lat", type=float, default=45.6910166666667, help="Latitude (types 1,4,18,21,22,23)") + p.add_argument("--lon", type=float, default=9.72357833333333, help="Longitude (alias for encoder --long)") + p.add_argument("--speed", type=float, default=0.1) + p.add_argument("--course", type=float, default=83.4) + p.add_argument("--ts", type=int, default=38) + p.add_argument("--status", type=int, default=15) + p.add_argument("--sart-msg", dest="sart_msg", default="SART ACTIVE") + p.add_argument("--fatdmaoffset", type=int, default=0) + p.add_argument("--fatdmaslots", type=int, default=0) + p.add_argument("--fatdmatimeout", type=int, default=0) + p.add_argument("--fatdmaincrement", type=int, default=0) + p.add_argument("--v_AtoN", action="store_true") + p.add_argument("--aid_type", type=int, default=0) + p.add_argument("--aid_name", default="@@@@@@@@@@@@@@@@@@@@") + p.add_argument("--vsize", default="90x14") + p.add_argument("--channel_a", type=int, default=2087) + p.add_argument("--channel_b", type=int, default=2088) + p.add_argument("--ne_lon", type=float, default=9.9) + p.add_argument("--ne_lat", type=float, default=45.8) + p.add_argument("--sw_lon", type=float, default=9.5) + p.add_argument("--sw_lat", type=float, default=45.5) + p.add_argument("--interval", type=int, default=1) + p.add_argument("--quiet", type=int, default=15) + p.add_argument("--part", default="A") + p.add_argument("--vname", default="NAN") + p.add_argument("--callsign", default="KC9CAF") + p.add_argument("--vtype", type=int, default=60) + p.add_argument("--no-nrzi", action="store_true", help="Output NRZ frame before NRZI (debug)") + p.add_argument("--bytes", action="store_true", help="Write packed bytes (MSB-first) to stdout (binary)") + p.add_argument("--print-payload", action="store_true", help="Print 0/1 PDU line before the frame") + + args = p.parse_args() + if enc is None: + p.error("AIVDM_Encoder.py не найден; CLI encode_to_nrzi недоступен.") + payload = build_payload(args) + if args.print_payload: + print(payload, file=sys.stderr) + bits = build_nrzi_frame(payload, enable_nrzi=not args.no_nrzi) + if args.bytes: + sys.stdout.buffer.write(nrzi_bits_to_bytes(bits)) + else: + print("".join(str(b) for b in bits)) + + +if __name__ == "__main__": + main() diff --git a/ais_nrzi_pipeline/phy.py b/ais_nrzi_pipeline/phy.py new file mode 100644 index 0000000..53445d2 --- /dev/null +++ b/ais_nrzi_pipeline/phy.py @@ -0,0 +1,229 @@ +# -*- coding: utf-8 -*- +""" +Physical-layer AIS frame: bit padding, CRC-16 (ITU, as in gr-aistx Build_Frame), +HDLC-style bit stuffing, flags, NRZI — ported from gr-aistx/lib/Build_Frame_impl.cc +""" + +from __future__ import annotations + +LEN_PREAMBLE = 24 +LEN_START = 8 +LEN_CRC = 16 +LEN_FRAME_MAX = 256 + +# CRC-16-CCITT table (same as Build_frame_impl.cc) +_CRC_ITU16_TABLE = ( + 0x0000, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF, + 0x8C48, 0x9DC1, 0xAF5A, 0xBED3, 0xCA6C, 0xDBE5, 0xE97E, 0xF8F7, + 0x1081, 0x0108, 0x3393, 0x221A, 0x56A5, 0x472C, 0x75B7, 0x643E, + 0x9CC9, 0x8D40, 0xBFDB, 0xAE52, 0xDAED, 0xCB64, 0xF9FF, 0xE876, + 0x2102, 0x308B, 0x0210, 0x1399, 0x6726, 0x76AF, 0x4434, 0x55BD, + 0xAD4A, 0xBCC3, 0x8E58, 0x9FD1, 0xEB6E, 0xFAE7, 0xC87C, 0xD9F5, + 0x3183, 0x200A, 0x1291, 0x0318, 0x77A7, 0x662E, 0x54B5, 0x453C, + 0xBDCB, 0xAC42, 0x9ED9, 0x8F50, 0xFBEF, 0xEA66, 0xD8FD, 0xC974, + 0x4204, 0x538D, 0x6116, 0x709F, 0x0420, 0x15A9, 0x2732, 0x36BB, + 0xCE4C, 0xDFC5, 0xED5E, 0xFCD7, 0x8868, 0x99E1, 0xAB7A, 0xBAF3, + 0x5285, 0x430C, 0x7197, 0x601E, 0x14A1, 0x0528, 0x37B3, 0x263A, + 0xDECD, 0xCF44, 0xFDDF, 0xEC56, 0x98E9, 0x8960, 0xBBFB, 0xAA72, + 0x6306, 0x728F, 0x4014, 0x519D, 0x2522, 0x34AB, 0x0630, 0x17B9, + 0xEF4E, 0xFEC7, 0xCC5C, 0xDDD5, 0xA96A, 0xB8E3, 0x8A78, 0x9BF1, + 0x7387, 0x620E, 0x5095, 0x411C, 0x35A3, 0x242A, 0x16B1, 0x0738, + 0xFFCF, 0xEE46, 0xDCDD, 0xCD54, 0xB9EB, 0xA862, 0x9AF9, 0x8B70, + 0x8408, 0x9581, 0xA71A, 0xB693, 0xC22C, 0xD3A5, 0xE13E, 0xF0B7, + 0x0840, 0x19C9, 0x2B52, 0x3ADB, 0x4E64, 0x5FED, 0x6D76, 0x7CFF, + 0x9489, 0x8500, 0xB79B, 0xA612, 0xD2AD, 0xC324, 0xF1BF, 0xE036, + 0x18C1, 0x0948, 0x3BD3, 0x2A5A, 0x5EE5, 0x4F6C, 0x7DF7, 0x6C7E, + 0xA50A, 0xB483, 0x8618, 0x9791, 0xE32E, 0xF2A7, 0xC03C, 0xD1B5, + 0x2942, 0x38CB, 0x0A50, 0x1BD9, 0x6F66, 0x7EEF, 0x4C74, 0x5DFD, + 0xB58B, 0xA402, 0x9699, 0x8710, 0xF3AF, 0xE226, 0xD0BD, 0xC134, + 0x39C3, 0x284A, 0x1AD1, 0x0B58, 0x7FE7, 0x6E6E, 0x5CF5, 0x4D7C, + 0xC60C, 0xD785, 0xE51E, 0xF497, 0x8028, 0x91A1, 0xA33A, 0xB2B3, + 0x4A44, 0x5BCD, 0x6956, 0x78DF, 0x0C60, 0x1DE9, 0x2F72, 0x3EFB, + 0xD68D, 0xC704, 0xF59F, 0xE416, 0x90A9, 0x8120, 0xB3BB, 0xA232, + 0x5AC5, 0x4B4C, 0x79D7, 0x685E, 0x1CE1, 0x0D68, 0x3FF3, 0x2E7A, + 0xE70E, 0xF687, 0xC41C, 0xD595, 0xA12A, 0xB0A3, 0x8238, 0x93B1, + 0x6B46, 0x7ACF, 0x4854, 0x59DD, 0x2D62, 0x3CEB, 0x0E70, 0x1FF9, + 0xF78F, 0xE606, 0xD49D, 0xC514, 0xB1AB, 0xA022, 0x92B9, 0x8330, + 0x7BC7, 0x6A4E, 0x58D5, 0x495C, 0x3DE3, 0x2C6A, 0x1EF1, 0x0F78, +) + + +def bits_from_payload_string(s: str) -> list[int]: + """ASCII '0'/'1' string -> list of 0/1 (same as Build_Frame ctor).""" + s = s.strip().replace(" ", "").replace("\n", "") + out: list[int] = [] + for c in s: + if c == "0": + out.append(0) + elif c == "1": + out.append(1) + else: + raise ValueError("Payload must be only 0 and 1 characters") + return out + + +def _pad_to_multiple_of_8(bits: list[int]) -> tuple[list[int], int]: + r = len(bits) % 8 + if r == 0: + return bits, 0 + pad = 8 - r + return bits + [0] * pad, pad + + +def reverse_bit_order(bits: list[int]) -> None: + """In-place: reverse bit order within each byte (8-bit group).""" + n = len(bits) + assert n % 8 == 0 + for i in range(n // 8): + base = i * 8 + for j in range(4): + a = base + j + b = base + 7 - j + bits[a], bits[b] = bits[b], bits[a] + + +def _compute_crc_bits(buffer_bits: list[int]) -> list[int]: + """ + Match Build_Frame_impl::compute_crc + int2bin/reverse/swap (16-bit FCS bits). + buffer_bits length must be multiple of 8. + """ + datalen = len(buffer_bits) // 8 + data = [] + for j in range(datalen): + v = 0 + for k in range(8): + v = (v << 1) | (buffer_bits[j * 8 + k] & 1) + data.append(v) + + crc = 0xFFFF + for b in data: + crc = ((crc >> 8) ^ _CRC_ITU16_TABLE[(crc ^ b) & 0xFF]) & 0xFFFF + crc = (crc & 0xFFFF) ^ 0xFFFF + + ret = ["0"] * 16 + buf_idx = 15 + a = crc & 0xFFFF + for _ in range(16): + ret[buf_idx] = "1" if (a & 1) else "0" + a >>= 1 + buf_idx -= 1 + + rb = [1 if c == "1" else 0 for c in ret] + reverse_bit_order(rb) + ret = ["1" if x else "0" for x in rb] + + buf_idx = 15 + a = crc & 0xFFFF + for _ in range(16): + ret[buf_idx] = "1" if (a & 1) else "0" + a >>= 1 + buf_idx -= 1 + + temp = ret[8:16] + ret[8:16] = ret[0:8] + ret[0:8] = temp + + return [1 if c == "1" else 0 for c in ret] + + +def bit_stuff(bits: list[int]) -> list[int]: + """HDLC-style: after five consecutive 1s insert a 0.""" + out: list[int] = [] + consecutive = 0 + for b in bits: + out.append(b) + if b & 1: + consecutive += 1 + if consecutive == 5: + out.append(0) + consecutive = 0 + else: + consecutive = 0 + return out + + +def nrz_to_nrzi(data: list[int]) -> None: + """In-place NRZI (same rule as nrz_to_nrzi_impl.cc / Build_Frame_impl).""" + prev = 0 + for i in range(len(data)): + nrz = data[i] & 1 + if nrz == 0: + nrzi = prev ^ 1 + else: + nrzi = prev + data[i] = nrzi + prev = nrzi + + +def _preamble_bits() -> list[int]: + return [1, 0] * (LEN_PREAMBLE // 2) + + +def _start_flag_bits() -> list[int]: + return [0, 1, 1, 1, 1, 1, 1, 0] + + +def build_nrzi_frame(payload_ascii: str, enable_nrzi: bool = True) -> list[int]: + """ + :param payload_ascii: AIS PDU as '0'/'1' string (output style of AIVDM_Encoder.py) + :param enable_nrzi: if False, return NRZ frame bits (before NRZI), for debugging + :return: list of 0/1 (length 256 for short PDUs, or dynamic for long) + """ + payload = bits_from_payload_string(payload_ascii) + payload, _ = _pad_to_multiple_of_8(payload) + len_payload = len(payload) + + crc_bits = _compute_crc_bits(payload) + full = payload + crc_bits + reverse_bit_order(full) + + stuffed = bit_stuff(full) + + preamble = _preamble_bits() + start = _start_flag_bits() + end = _start_flag_bits() + + if len_payload <= 168: + frame = [0] * LEN_FRAME_MAX + idx = 0 + frame[idx:idx + LEN_PREAMBLE] = preamble + idx += LEN_PREAMBLE + frame[idx:idx + LEN_START] = start + idx += LEN_START + frame[idx:idx + len(stuffed)] = stuffed + idx += len(stuffed) + frame[idx:idx + 8] = end + idx += 8 + # padding to 256 + assert idx <= LEN_FRAME_MAX + if enable_nrzi: + nrz_to_nrzi(frame) + return frame + + len_frame = LEN_PREAMBLE + LEN_START * 2 + len(stuffed) + while len_frame % 8 != 0: + len_frame += 1 + frame = [0] * len_frame + idx = 0 + frame[idx:idx + LEN_PREAMBLE] = preamble + idx += LEN_PREAMBLE + frame[idx:idx + LEN_START] = start + idx += LEN_START + frame[idx:idx + len(stuffed)] = stuffed + idx += len(stuffed) + frame[idx:idx + 8] = end + if enable_nrzi: + nrz_to_nrzi(frame) + return frame + + +def nrzi_bits_to_bytes(bits: list[int]) -> bytes: + """Pack MSB-first within each byte (same as byte_packing in Build_Frame).""" + assert len(bits) % 8 == 0 + out = bytearray() + for i in range(len(bits) // 8): + b = 0 + for k in range(8): + b = b * 2 + (bits[i * 8 + k] & 1) + out.append(b & 0xFF) + return bytes(out) diff --git a/ais_phy.py b/ais_phy.py new file mode 100644 index 0000000..7e265ce --- /dev/null +++ b/ais_phy.py @@ -0,0 +1,178 @@ +""" +Формирование кадра VDL AIS (флаги HDLC, bit stuffing, CRC-16-CCITT) и NRZI. +Опционально: выравнивание payload до октета и добор NRZ до N бит перед NRZI (аналог padd_frame в GNU Radio). +""" +from __future__ import annotations + +from typing import Optional + +from bitarray import bitarray + +FLAG = bitarray("01111110") + + +def _bits_to_bytes_msb(bits: bitarray) -> bytes: + out = bytearray() + for i in range(0, len(bits), 8): + chunk = bits[i : i + 8] + if len(chunk) < 8: + pad = bitarray(8 - len(chunk)) + pad.setall(0) + chunk = chunk + pad + v = 0 + for b in chunk: + v = (v << 1) | int(b) + out.append(v) + return bytes(out) + + +def crc16_ccitt_fcs(data_bits: bitarray) -> int: + """FCS по данным payload (без FCS), длина кратна 8 бит.""" + b = _bits_to_bytes_msb(data_bits) + crc = 0xFFFF + poly = 0x1021 + for byte in b: + crc ^= byte << 8 + for _ in range(8): + if crc & 0x8000: + crc = ((crc << 1) ^ poly) & 0xFFFF + else: + crc = (crc << 1) & 0xFFFF + return crc ^ 0xFFFF + + +def _int_to_bits(val: int, n: int) -> bitarray: + out = bitarray() + for i in range(n - 1, -1, -1): + out.append((val >> i) & 1) + return out + + +def bit_stuff(bits: bitarray) -> bitarray: + out = bitarray() + count = 0 + for b in bits: + out.append(b) + if b: + count += 1 + if count == 5: + out.append(0) + count = 0 + else: + count = 0 + return out + + +def _pad_payload_to_octet_boundary(payload_bits: bitarray) -> bitarray: + r = len(payload_bits) % 8 + if r == 0: + return payload_bits + z = bitarray(8 - r) + z.setall(0) + return payload_bits + z + + +def _pad_nrz_to_min_bits(nrz_bits: bitarray, min_len: int) -> bitarray: + if min_len <= 0 or len(nrz_bits) >= min_len: + return nrz_bits + tail = bitarray(min_len - len(nrz_bits)) + tail.setall(0) + return nrz_bits + tail + + +def build_hdlc_frame( + payload_bits: bitarray, *, pad_payload_to_octet: bool = False +) -> bitarray: + """payload_bits — двоичное тело AIS (как pyais Payload.to_bitarray()).""" + pl = payload_bits + if len(pl) % 8 != 0: + if pad_payload_to_octet: + pl = _pad_payload_to_octet_boundary(pl) + else: + raise ValueError("AIS payload length must be multiple of 8 bits for this CRC path") + fcs = crc16_ccitt_fcs(pl) + fcs_bits = _int_to_bits(fcs, 16) + data = pl + fcs_bits + stuffed = bit_stuff(data) + return FLAG + stuffed + FLAG + + +def nrzi_encode(bits: bitarray) -> bitarray: + """NRZI: 1 — уровень без изменения, 0 — переключение.""" + level = 0 + out = bitarray() + for b in bits: + if not int(b): + level = 1 - level + out.append(level) + return out + + +def preamble_alternating(num_bits: int = 24) -> bitarray: + """ + ITU-style dotting: чередование 1/0 (101010…), как gr-aistx LEN_PREAMBLE и phy._preamble_bits. + Вариант 010101… дал бы после NRZI (packed, старт уровня 0) байты 0xCC… вместо 0x66… — та же частота, + другая фаза; к стаффингу это не относится. + """ + out = bitarray() + for i in range(num_bits): + out.append(1 - (i & 1)) + return out + + +def hdlc_nrzi_bytes_no_preamble( + payload_bits: bitarray, + *, + nrzi_byte_mode: str = "packed", + pad_payload_to_octet: bool = False, + pad_nrz_total_bits: Optional[int] = None, +) -> bytes: + """Только HDLC+NRZI без преамбулы. pad_nrz_total_bits — добор нулей в NRZ до длины (как GNU Radio padd_frame).""" + frame = build_hdlc_frame(payload_bits, pad_payload_to_octet=pad_payload_to_octet) + frame = _pad_nrz_to_min_bits(frame, pad_nrz_total_bits or 0) + nrz = nrzi_encode(frame) + if nrzi_byte_mode == "expanded": + return bytes(0xFF if int(b) else 0x00 for b in nrz) + return _bits_to_bytes_msb(nrz) + + +def phy_frame_bit_counts( + payload_bits: bitarray, *, pad_payload_to_octet: bool = False +) -> dict: + """Длины для отладки: payload, поле между флагами (со стаффингом), полный HDLC.""" + pl_in = len(payload_bits) + pl = payload_bits + if len(pl) % 8 != 0 and pad_payload_to_octet: + pl = _pad_payload_to_octet_boundary(pl) + frame = build_hdlc_frame(pl, pad_payload_to_octet=pad_payload_to_octet) + inner = frame[8:-8] + return { + "payload_bits_input": pl_in, + "payload_bits_after_pad": len(pl), + "bits_between_flags_stuffed": len(inner), + "hdlc_frame_bits": len(frame), + } + + +def ais_channel_to_nrzi_bytes( + payload_bits: bitarray, + *, + preamble_bits: int = 24, + nrzi_byte_mode: str = "packed", + pad_payload_to_octet: bool = False, + pad_nrz_total_bits: Optional[int] = None, +) -> bytes: + """ + nrzi_byte_mode: + 'packed' — 8 NRZI-сэмплов в байт (MSB первый); + 'expanded' — один байт на NRZI-бит: 0x00 / 0xFF. + pad_nrz_total_bits — после преамбулы+кадра дописать нули в NRZ до этой длины (типично 200 для GNU Radio). + """ + frame = build_hdlc_frame(payload_bits, pad_payload_to_octet=pad_payload_to_octet) + pre = preamble_alternating(preamble_bits) if preamble_bits > 0 else bitarray() + combined = pre + frame + combined = _pad_nrz_to_min_bits(combined, pad_nrz_total_bits or 0) + nrzi = nrzi_encode(combined) + if nrzi_byte_mode == "expanded": + return bytes(0xFF if int(b) else 0x00 for b in nrzi) + return _bits_to_bytes_msb(nrzi) diff --git a/download_tiles.py b/download_tiles.py new file mode 100644 index 0000000..d60dd81 --- /dev/null +++ b/download_tiles.py @@ -0,0 +1,261 @@ +#!/usr/bin/env python3 +""" +Скрипт для скачивания тайлов карты OpenStreetMap для офлайн использования. +Соблюдает политику использования тайлов OpenStreetMap: +- Использует корректный User-Agent +- Соблюдает минимальную задержку 1 секунда между запросами +- Обрабатывает ошибки rate limiting + +Использование: + python download_tiles.py --min-zoom 5 --max-zoom 12 --bounds 55.5,37.5,56.0,38.0 +""" + +import os +import math +import argparse +import requests +from pathlib import Path +import time + + +def deg2num(lat_deg, lon_deg, zoom): + """Преобразует координаты в номер тайла""" + lat_rad = math.radians(lat_deg) + n = 2.0 ** zoom + xtile = int((lon_deg + 180.0) / 360.0 * n) + ytile = int((1.0 - math.asinh(math.tan(lat_rad)) / math.pi) / 2.0 * n) + return (xtile, ytile) + + +def num2deg(xtile, ytile, zoom): + """Преобразует номер тайла в координаты""" + n = 2.0 ** zoom + lon_deg = xtile / n * 360.0 - 180.0 + lat_rad = math.atan(math.sinh(math.pi * (1 - 2 * ytile / n))) + lat_deg = math.degrees(lat_rad) + return (lat_deg, lon_deg) + + +def download_tile(z, x, y, output_dir, retries=3, delay=1.0): + """Скачивает один тайл с соблюдением политики использования OpenStreetMap""" + tile_dir = os.path.join(output_dir, str(z), str(x)) + os.makedirs(tile_dir, exist_ok=True) + + tile_path = os.path.join(tile_dir, f"{y}.png") + + # Если тайл уже существует, пропускаем + if os.path.exists(tile_path): + return True + + # URL для скачивания тайла + # Используем разные поддомены для распределения нагрузки + subdomains = ['a', 'b', 'c'] + subdomain = subdomains[(x + y) % len(subdomains)] + url = f"https://{subdomain}.tile.openstreetmap.org/{z}/{x}/{y}.png" + + # Заголовки согласно политике использования OpenStreetMap + headers = { + 'User-Agent': 'PythonAisMap/1.0 (Offline Map Tile Downloader; contact: localhost)', + 'Referer': 'http://localhost:8000/' + } + + for attempt in range(retries): + try: + response = requests.get(url, headers=headers, timeout=30) + + if response.status_code == 200: + with open(tile_path, 'wb') as f: + f.write(response.content) + time.sleep(delay) # Задержка между запросами (минимум 1 сек согласно политике) + return True + elif response.status_code == 404: + # Тайл не существует (например, для океана) + return False + elif response.status_code == 429: + # Too Many Requests - нужно увеличить задержку + wait_time = delay * (2 ** attempt) + print(f" Превышен лимит запросов для {z}/{x}/{y}, ожидание {wait_time} сек...") + time.sleep(wait_time) + continue + elif response.status_code == 403: + # Forbidden - возможно, нарушена политика использования + print(f" Доступ запрещен для {z}/{x}/{y}. Проверьте User-Agent и задержки.") + return False + else: + print(f" Неожиданный статус {response.status_code} для {z}/{x}/{y}") + if attempt < retries - 1: + time.sleep(delay * (attempt + 1)) + else: + return False + except requests.exceptions.Timeout: + if attempt < retries - 1: + print(f" Таймаут для {z}/{x}/{y}, повторная попытка...") + time.sleep(delay * (attempt + 1)) + else: + print(f" Ошибка таймаута при скачивании тайла {z}/{x}/{y}") + return False + except Exception as e: + if attempt < retries - 1: + time.sleep(delay * (attempt + 1)) + else: + print(f" Ошибка при скачивании тайла {z}/{x}/{y}: {e}") + return False + + return False + + +def download_tiles(min_lat, min_lon, max_lat, max_lon, min_zoom, max_zoom, output_dir="static/tiles", delay=1.0): + """Скачивает тайлы для указанной области и уровней масштабирования""" + print(f"Начинаем скачивание тайлов...") + print(f"Область: ({min_lat}, {min_lon}) - ({max_lat}, {max_lon})") + print(f"Уровни масштабирования: {min_zoom} - {max_zoom}") + print(f"Выходная папка: {output_dir}") + + total_tiles = 0 + downloaded_tiles = 0 + skipped_tiles = 0 + + for z in range(min_zoom, max_zoom + 1): + print(f"\nОбработка уровня масштабирования {z}...") + + # Определяем диапазон тайлов для данного уровня + min_tile_x, max_tile_y = deg2num(min_lat, min_lon, z) + max_tile_x, min_tile_y = deg2num(max_lat, max_lon, z) + + # Убеждаемся, что координаты в правильном порядке + if min_tile_x > max_tile_x: + min_tile_x, max_tile_x = max_tile_x, min_tile_x + if min_tile_y > max_tile_y: + min_tile_y, max_tile_y = max_tile_y, min_tile_y + + level_tiles = (max_tile_x - min_tile_x + 1) * (max_tile_y - min_tile_y + 1) + total_tiles += level_tiles + + print(f" Тайлов на уровне {z}: {level_tiles} ({min_tile_x}-{max_tile_x}, {min_tile_y}-{max_tile_y})") + + level_downloaded = 0 + level_skipped = 0 + + for x in range(min_tile_x, max_tile_x + 1): + for y in range(min_tile_y, max_tile_y + 1): + if download_tile(z, x, y, output_dir, delay=delay): + level_downloaded += 1 + downloaded_tiles += 1 + else: + level_skipped += 1 + skipped_tiles += 1 + + # Показываем прогресс каждые 10 тайлов + if (level_downloaded + level_skipped) % 10 == 0: + print(f" Прогресс: {level_downloaded + level_skipped}/{level_tiles} тайлов") + + print(f" Уровень {z} завершен: скачано {level_downloaded}, пропущено {level_skipped}") + + print(f"\n=== Итоги ===") + print(f"Всего тайлов: {total_tiles}") + print(f"Скачано: {downloaded_tiles}") + print(f"Пропущено: {skipped_tiles}") + print(f"Готово!") + + +def main(): + parser = argparse.ArgumentParser( + description="Скачивание тайлов OpenStreetMap для офлайн использования" + ) + parser.add_argument( + "--min-zoom", + type=int, + default=5, + help="Минимальный уровень масштабирования (по умолчанию: 5)" + ) + parser.add_argument( + "--max-zoom", + type=int, + default=12, + help="Максимальный уровень масштабирования (по умолчанию: 12)" + ) + parser.add_argument( + "--delay", + type=float, + default=1.0, + help="Задержка между запросами в секундах (по умолчанию: 1.0, минимум рекомендуется 1.0)" + ) + parser.add_argument( + "--bounds", + type=str, + help="Границы области в формате: min_lat,min_lon,max_lat,max_lon (например: 55.5,37.5,56.0,38.0)" + ) + parser.add_argument( + "--center", + type=str, + help="Центр области и радиус в формате: lat,lon,radius_km (например: 55.75,37.62,50)" + ) + parser.add_argument( + "--output", + type=str, + default="static/tiles", + help="Выходная папка для тайлов (по умолчанию: static/tiles)" + ) + + args = parser.parse_args() + + # Определяем границы области + if args.bounds: + parts = args.bounds.split(",") + if len(parts) != 4: + print("Ошибка: --bounds должен содержать 4 значения: min_lat,min_lon,max_lat,max_lon") + return + min_lat, min_lon, max_lat, max_lon = map(float, parts) + elif args.center: + parts = args.center.split(",") + if len(parts) != 3: + print("Ошибка: --center должен содержать 3 значения: lat,lon,radius_km") + return + center_lat, center_lon, radius_km = map(float, parts) + # Приблизительно вычисляем границы на основе радиуса + # 1 градус широты ≈ 111 км + lat_delta = radius_km / 111.0 + # 1 градус долготы зависит от широты + lon_delta = radius_km / (111.0 * math.cos(math.radians(center_lat))) + min_lat = center_lat - lat_delta + max_lat = center_lat + lat_delta + min_lon = center_lon - lon_delta + max_lon = center_lon + lon_delta + else: + # По умолчанию: Москва и окрестности + print("Используются границы по умолчанию: Москва и окрестности") + min_lat, min_lon = 55.5, 37.3 + max_lat, max_lon = 56.0, 37.9 + + # Проверяем валидность границ + if min_lat >= max_lat or min_lon >= max_lon: + print("Ошибка: неверные границы области") + return + + if args.min_zoom < 0 or args.max_zoom > 19 or args.min_zoom > args.max_zoom: + print("Ошибка: неверные уровни масштабирования (должны быть от 0 до 19, min <= max)") + return + + # Создаем выходную папку + os.makedirs(args.output, exist_ok=True) + + # Проверяем задержку (рекомендуется минимум 1 секунда) + if args.delay < 1.0: + print("ВНИМАНИЕ: Задержка менее 1 секунды может привести к блокировке!") + print("Рекомендуется использовать задержку не менее 1.0 секунды.") + response = input("Продолжить с текущей задержкой? (y/n): ") + if response.lower() != 'y': + return + + # Скачиваем тайлы + download_tiles( + min_lat, min_lon, max_lat, max_lon, + args.min_zoom, args.max_zoom, + args.output, + args.delay + ) + + +if __name__ == "__main__": + main() + diff --git a/main.py b/main.py new file mode 100644 index 0000000..79036d7 --- /dev/null +++ b/main.py @@ -0,0 +1,60 @@ +import os +import threading +from urllib.request import urlopen +from urllib.error import URLError + +from routes import app, AIS_HUB_URL +from ssl_utils import get_ssl_context, run_http_redirect, _get_local_ips + + +def _check_ais_hub(): + """Однократная проверка доступности ais_hub на старте (не критично).""" + try: + resp = urlopen(f"{AIS_HUB_URL}/api/v1/health", timeout=2.0) + if getattr(resp, "status", 200) == 200: + print(f"[ais_hub] OK: {AIS_HUB_URL}") + return + print(f"[ais_hub] unexpected status {resp.status} from {AIS_HUB_URL}") + except URLError as e: + print(f"[ais_hub] WARNING: {AIS_HUB_URL} недоступен ({e.reason}). " + f"AIS-данные и /ws не будут работать, пока ais_hub не поднят.") + except Exception as e: + print(f"[ais_hub] WARNING: проверка {AIS_HUB_URL} не удалась: {e}") + + +if __name__ == "__main__": + _check_ais_hub() + + # Геолокация в браузере (телефон) требует secure context (HTTPS или localhost). + # Поэтому HTTPS можно включать/выключать флагом окружения. + use_https = os.environ.get("AISMAP_HTTPS", "1").strip().lower() not in ("0", "false", "no", "off") + + if use_https: + ssl_ctx = get_ssl_context() + else: + ssl_ctx = None + + if ssl_ctx: + https_port = int(os.environ.get("AISMAP_HTTPS_PORT", "443")) + enable_redirect = os.environ.get("AISMAP_HTTP_REDIRECT", "1").strip().lower() not in ("0", "false", "no", "off") + if enable_redirect: + redir_thread = threading.Thread(target=run_http_redirect, args=(https_port,), daemon=True) + redir_thread.start() + + local_ips = _get_local_ips() + print(f"[HTTPS] Сервер запускается на порту {https_port}") + for ip in local_ips: + port_suffix = f":{https_port}" if https_port != 443 else "" + print(f"[HTTPS] Карта: https://{ip}{port_suffix}/") + print(f"[HTTPS] Сертификат: https://{ip}{port_suffix}/cert") + print("[HTTPS] Чтобы убрать предупреждение — откройте /cert и установите CA-сертификат") + + app.run(host="0.0.0.0", port=https_port, debug=True, + use_reloader=False, threaded=True, ssl_context=ssl_ctx) + else: + print("[HTTP] Запуск без HTTPS (геолокация телефона в браузере работать не будет)") + local_ips = _get_local_ips() + for ip in local_ips: + print(f"[HTTP] Карта: http://{ip}/") + app.run(host="0.0.0.0", port=80, debug=True, + use_reloader=False, threaded=True) diff --git a/mock_data/base_stations.json b/mock_data/base_stations.json new file mode 100644 index 0000000..fef0bf0 --- /dev/null +++ b/mock_data/base_stations.json @@ -0,0 +1,16 @@ +[ + { + "lat": 59.898667, + "lon": 29.866833, + "mmsi": 2734450, + "msg_type": 4, + "timestamp": 1776335118 + }, + { + "lat": 59.873513, + "lon": 30.197913, + "mmsi": 352006264, + "msg_type": 4, + "timestamp": 1776334710 + } +] diff --git a/mock_data/buoys.json b/mock_data/buoys.json new file mode 100644 index 0000000..2d4fc9b --- /dev/null +++ b/mock_data/buoys.json @@ -0,0 +1,38 @@ +[ + { + "aton_type": 25, + "lat": 59.968, + "lon": 29.781333, + "mmsi": 992736009, + "msg_type": 21, + "name": "21", + "timestamp": 1776335021 + }, + { + "aton_type": 14, + "lat": 59.9765, + "lon": 29.764833, + "mmsi": 992736007, + "msg_type": 21, + "name": "17", + "timestamp": 1776335020 + }, + { + "aton_type": 14, + "lat": 59.973333, + "lon": 29.771167, + "mmsi": 992736008, + "msg_type": 21, + "name": "19", + "timestamp": 1776335019 + }, + { + "aton_type": 14, + "lat": 59.980333, + "lon": 29.752667, + "mmsi": 992736006, + "msg_type": 21, + "name": "13", + "timestamp": 1776335020 + } +] diff --git a/mock_data/vessels.json b/mock_data/vessels.json new file mode 100644 index 0000000..5b2f8be --- /dev/null +++ b/mock_data/vessels.json @@ -0,0 +1,81 @@ +[ + { + "callsign": "UCUA6", + "course": 360.0, + "heading": 511.0, + "lat": 91.0, + "lon": 181.0, + "mmsi": 273251120, + "rot": -128.0, + "shipname": "VALENTIN RYKOV", + "shiptype": 89, + "speed": 102.3, + "timestamp": 1776335052, + "to_bow": 15, + "to_port": 2, + "to_starboard": 12, + "to_stern": 59, + "vessel_class": "A" + }, + { + "course": 360.0, + "heading": 86.0, + "lat": 91.0, + "lon": 181.0, + "mmsi": 671575100, + "rot": 0.0, + "speed": 102.3, + "timestamp": 1776335119, + "vessel_class": "A" + }, + { + "callsign": "UAQN", + "course": 360.0, + "heading": 511.0, + "lat": 91.0, + "lon": 181.0, + "mmsi": 273315900, + "rot": -128.0, + "shipname": "KAPITAN PLAKHIN", + "shiptype": 90, + "speed": 102.3, + "timestamp": 1776335046, + "to_bow": 25, + "to_port": 7, + "to_starboard": 9, + "to_stern": 53, + "vessel_class": "A" + }, + { + "callsign": "UAPB6", + "course": 360.0, + "heading": 511.0, + "lat": 59.943828, + "lon": 30.189773, + "mmsi": 273262700, + "rot": -128.0, + "shipname": "CASTOR", + "shiptype": 30, + "speed": 0.0, + "timestamp": 1776335125, + "to_bow": 46, + "to_port": 6, + "to_starboard": 8, + "to_stern": 24, + "vessel_class": "A" + }, + { + "callsign": null, + "course": 360.0, + "heading": 511.0, + "lat": 91.0, + "lon": 181.0, + "mmsi": 0, + "rot": -128.0, + "shipname": null, + "shiptype": 37, + "speed": 102.3, + "timestamp": 1776335124, + "vessel_class": "A" + } +] diff --git a/network_manager.py b/network_manager.py new file mode 100644 index 0000000..7fd48b8 --- /dev/null +++ b/network_manager.py @@ -0,0 +1,150 @@ +import os +import json +import subprocess +import threading + +NETWORK_CONFIG_PATH = "/etc/aismap/network.json" +NETWORK_SCRIPTS_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "scripts") + +NETWORK_DEFAULTS = { + "mode": "ap", + "wifi_ssid": "", + "wifi_psk": "", + "wifi_ip": "192.168.22.50/24", + "wifi_gw": "192.168.22.1", + "wifi_dns": "8.8.8.8", + "ap_ip": "192.168.4.1/24", + "ap_ssid": "", + "ap_psk": "", + "iface": "wlan0", +} +HOSTAPD_CONF = "/etc/hostapd/hostapd.conf" +network_config_lock = threading.Lock() + + +def _read_hostapd_conf() -> dict: + """Reads SSID, passphrase and interface from the real hostapd.conf.""" + result = {} + try: + with open(HOSTAPD_CONF, "r") as f: + for line in f: + line = line.strip() + if line.startswith("#") or "=" not in line: + continue + k, v = line.split("=", 1) + k, v = k.strip(), v.strip() + if k == "ssid": + result["ap_ssid"] = v + elif k == "wpa_passphrase": + result["ap_psk"] = v + elif k == "interface": + result["iface"] = v + except (FileNotFoundError, PermissionError): + pass + return result + + +def load_network_config() -> dict: + merged = dict(NETWORK_DEFAULTS) + hostapd_vals = _read_hostapd_conf() + merged.update(hostapd_vals) + try: + with open(NETWORK_CONFIG_PATH, "r") as f: + cfg = json.load(f) + merged.update(cfg) + except (FileNotFoundError, json.JSONDecodeError, PermissionError): + pass + return merged + + +def save_network_config(cfg: dict): + os.makedirs(os.path.dirname(NETWORK_CONFIG_PATH), exist_ok=True) + with open(NETWORK_CONFIG_PATH, "w") as f: + json.dump(cfg, f, indent=2) + + +def get_current_network_info() -> dict: + """Reads live network state from the OS (Linux only).""" + info = {"ip": None, "ssid": None, "mode": None, "iface": None} + cfg = load_network_config() + iface = cfg.get("iface", "wlan0") + info["iface"] = iface + + try: + out = subprocess.check_output( + ["ip", "-4", "addr", "show", iface], timeout=5, stderr=subprocess.DEVNULL + ).decode() + for line in out.splitlines(): + line = line.strip() + if line.startswith("inet "): + info["ip"] = line.split()[1] + break + except Exception: + pass + + try: + out = subprocess.check_output( + ["iw", "dev", iface, "info"], timeout=5, stderr=subprocess.DEVNULL + ).decode() + for line in out.splitlines(): + line = line.strip() + if line.startswith("ssid"): + info["ssid"] = line.split(None, 1)[1] + if line.startswith("type"): + tp = line.split(None, 1)[1].lower() + if "ap" in tp: + info["mode"] = "ap" + elif "managed" in tp or "station" in tp: + info["mode"] = "wifi" + except Exception: + pass + + if info["mode"] is None: + try: + subprocess.check_output( + ["pgrep", "-x", "hostapd"], timeout=3, stderr=subprocess.DEVNULL + ) + info["mode"] = "ap" + except Exception: + try: + subprocess.check_output( + ["pgrep", "-x", "wpa_supplicant"], timeout=3, stderr=subprocess.DEVNULL + ) + info["mode"] = "wifi" + except Exception: + info["mode"] = cfg.get("mode", "ap") + + return info + + +def switch_network_mode(target_mode: str) -> dict: + """Runs the appropriate script to switch AP<->WiFi. Returns result dict.""" + if target_mode not in ("ap", "wifi"): + return {"ok": False, "error": "Invalid mode, must be 'ap' or 'wifi'"} + + script_name = "to_ap.sh" if target_mode == "ap" else "to_wifi.sh" + script_path = os.path.join(NETWORK_SCRIPTS_DIR, script_name) + + if not os.path.isfile(script_path): + return {"ok": False, "error": f"Script not found: {script_path}"} + + try: + proc = subprocess.run( + ["bash", script_path], + capture_output=True, text=True, timeout=30, + ) + if proc.returncode == 0: + cfg = load_network_config() + cfg["mode"] = target_mode + save_network_config(cfg) + return {"ok": True, "output": proc.stdout[-500:] if proc.stdout else ""} + else: + return { + "ok": False, + "error": f"Script exited with code {proc.returncode}", + "output": (proc.stdout or "") + (proc.stderr or ""), + } + except subprocess.TimeoutExpired: + return {"ok": False, "error": "Script timed out (30s)"} + except Exception as e: + return {"ok": False, "error": str(e)} diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..2b34ef1 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +Flask>=2.3.0 +flask-sock>=0.7.0 +websocket-client>=1.6.0 +requests>=2.31.0 +cryptography>=41.0.0 diff --git a/routes.py b/routes.py new file mode 100644 index 0000000..1c2b6a2 --- /dev/null +++ b/routes.py @@ -0,0 +1,937 @@ +import os +import time +import socket +import subprocess +import json +import threading +import hashlib +from urllib.request import urlopen, Request +from urllib.parse import urlencode +from urllib.error import URLError, HTTPError + +from flask import Flask, jsonify, render_template, send_from_directory, send_file, request, Response + +try: + from flask_sock import Sock +except ImportError: + Sock = None + +try: + from websocket import create_connection as _ws_create_connection + from websocket import WebSocketConnectionClosedException as _WsClosed +except ImportError: + _ws_create_connection = None + _WsClosed = Exception + +from state import get_cpu_usage_percent +from network_manager import ( + load_network_config, save_network_config, get_current_network_info, + switch_network_mode, network_config_lock, NETWORK_DEFAULTS, +) +from terminal import terminal_session +from transponder import ( + load_transponder_config, + normalize_transponder_config, + save_transponder_config, + merge_transponder_request, + build_preview, + send_transmission, + build_slot_udp_payload, + is_aistx_phy_available, + parse_nrzi_hex, + send_raw_nrzi_packet, + tx_gpio_pulse, +) + +app = Flask(__name__) +sock = Sock(app) if Sock else None + + +# ==================== ais_hub upstream ==================== + +AIS_HUB_URL = os.environ.get("AIS_HUB_URL", "http://127.0.0.1:8081").rstrip("/") + + +def _ais_hub_ws_url(): + base = AIS_HUB_URL + if base.startswith("https://"): + return "wss://" + base[len("https://"):] + "/ws" + if base.startswith("http://"): + return "ws://" + base[len("http://"):] + "/ws" + return "ws://" + base + "/ws" + + +# ==================== helpers ==================== + +def _append_client_log_line(line: str): + """Append a single line to client_errors.log (best-effort).""" + try: + path = os.path.join(os.path.dirname(__file__), "client_errors.log") + with open(path, "a", encoding="utf-8") as f: + f.write(line.rstrip("\n") + "\n") + except Exception: + pass + + +# ==================== WebSocket terminal ==================== +if sock is not None and os.name == "posix": + + @sock.route("/ws/terminal") + def ws_terminal(ws): + terminal_session(ws) + + +# ==================== WebSocket proxy to ais_hub /ws ==================== +if sock is not None: + + @sock.route("/ws") + def ws_ais_hub_proxy(ws): + """Прозрачный WS-прокси к ais_hub /ws (localhost:8081).""" + if _ws_create_connection is None: + # Не закрываем WS сразу: иначе браузер будет переподключаться в цикле и шуметь в консоли. + # Держим соединение открытым и периодически напоминаем про отсутствующую зависимость. + while True: + try: + ws.send(json.dumps({"type": "error", "ts": time.time(), + "data": {"error": "websocket-client not installed on server"}})) + except Exception: + return + try: + time.sleep(5) + except Exception: + return + + upstream_url = _ais_hub_ws_url() + # Если ais_hub временно недоступен, не рвём соединение с браузером: + # держим WS открытым и периодически ретраим подключение к upstream. + upstream = None + backoff = 1.0 + while upstream is None: + try: + upstream = _ws_create_connection(upstream_url, timeout=5) + break + except Exception as e: + try: + ws.send(json.dumps({"type": "error", "ts": time.time(), + "data": {"error": f"ais_hub unreachable: {e}"}})) + except Exception: + # Клиент ушёл — дальше ретраить нет смысла. + return + try: + time.sleep(backoff) + except Exception: + return + backoff = min(backoff * 2.0, 10.0) + + stop = threading.Event() + + def upstream_to_client(): + try: + while not stop.is_set(): + try: + msg = upstream.recv() + except _WsClosed: + break + except Exception: + break + if msg is None or msg == "": + break + if isinstance(msg, bytes): + try: + msg = msg.decode("utf-8", errors="ignore") + except Exception: + continue + try: + ws.send(msg) + except Exception: + break + finally: + stop.set() + + t = threading.Thread(target=upstream_to_client, daemon=True) + t.start() + + try: + while not stop.is_set(): + try: + # flask_sock/simple_websocket have slightly different receive() signatures + # across versions; some don't support timeout=, and some raise on timeout. + try: + data = ws.receive(timeout=30) + except TypeError: + data = ws.receive() + except Exception as e: + # Don't tear down the whole proxy on a benign receive timeout. + if "timeout" in str(e).lower(): + continue + break + if data is None: + break + try: + if isinstance(data, bytes): + upstream.send_binary(data) + else: + upstream.send(data) + except Exception: + break + finally: + stop.set() + try: + upstream.close() + except Exception: + pass + + +# ==================== Pages ==================== + +@app.route("/") +def index(): + return render_template("index.html") + + +@app.route("/cert") +def cert_install_page(): + return render_template("cert.html") + + +# ==================== REST proxy to ais_hub /api/v1/* ==================== + +_HOP_HEADERS = { + "connection", "keep-alive", "proxy-authenticate", "proxy-authorization", + "te", "trailers", "transfer-encoding", "upgrade", "content-encoding", + "content-length", "host", +} + + +@app.route("/api/v1/", methods=["GET", "POST", "PUT", "DELETE"]) +def proxy_ais_hub(rest): + """Прозрачный прокси REST /api/v1/* на ais_hub (127.0.0.1:8081).""" + url = f"{AIS_HUB_URL}/api/v1/{rest}" + qs = request.query_string.decode("utf-8") if request.query_string else "" + if qs: + url = f"{url}?{qs}" + + method = request.method.upper() + body = None + if method in ("POST", "PUT"): + body = request.get_data() or b"" + + headers = {} + ct = request.headers.get("Content-Type") + if ct: + headers["Content-Type"] = ct + + req = Request(url, data=body, method=method, headers=headers) + timeout = 10.0 if method in ("POST", "PUT") else 5.0 + + try: + resp = urlopen(req, timeout=timeout) + payload = resp.read() + status = getattr(resp, "status", 200) or 200 + out_headers = {} + for k, v in resp.headers.items(): + if k.lower() in _HOP_HEADERS: + continue + out_headers[k] = v + out_headers.setdefault("Cache-Control", "no-store") + return Response(payload, status=status, headers=out_headers) + except HTTPError as e: + try: + payload = e.read() + except Exception: + payload = b"" + out_headers = {} + try: + for k, v in e.headers.items(): + if k.lower() in _HOP_HEADERS: + continue + out_headers[k] = v + except Exception: + pass + out_headers.setdefault("Cache-Control", "no-store") + return Response(payload, status=e.code, headers=out_headers) + except URLError as e: + return jsonify({"error": f"ais_hub unreachable: {e.reason}"}), 503 + except Exception as e: + return jsonify({"error": f"proxy error: {e}"}), 502 + + +# ==================== API ==================== + +@app.route("/api/terminal") +def api_terminal(): + """Доступность веб-консоли (PTY + WebSocket).""" + return jsonify({"pty": os.name == "posix", "ws": sock is not None}) + + +@app.route("/api/version") +def api_version(): + """Версия/сборка для отладки кэша фронта и деплоя.""" + try: + here = os.path.dirname(__file__) + def _mtime(p): + try: + return int(os.path.getmtime(os.path.join(here, p))) + except Exception: + return None + return jsonify({ + "server_time": int(time.time()), + "routes_py_mtime": _mtime("routes.py"), + "app_js_mtime": _mtime(os.path.join("static", "js", "app.js")), + "index_html_mtime": _mtime(os.path.join("templates", "index.html")), + }) + except Exception as e: + return jsonify({"server_time": int(time.time()), "error": str(e)}) + + +@app.route("/api/sysinfo") +def api_sysinfo(): + """Локальная система: CPU %, температура, память, uptime. ais_hub этого не знает.""" + now = int(time.time()) + out = {"server_now": now} + + try: + with open("/proc/uptime", "r") as f: + out["sys_uptime"] = int(float(f.read().split()[0])) + except Exception: + out["sys_uptime"] = None + + try: + with open("/sys/class/thermal/thermal_zone0/temp", "r") as f: + out["cpu_temp"] = round(int(f.read().strip()) / 1000.0, 1) + except Exception: + out["cpu_temp"] = None + + out["cpu_percent"] = get_cpu_usage_percent() + + try: + mi = {} + with open("/proc/meminfo", "r") as f: + for line in f: + k, v = line.split(":", 1) + mi[k.strip()] = int(v.strip().split()[0]) + total = mi.get("MemTotal", 0) + avail = mi.get("MemAvailable", mi.get("MemFree", 0)) + out["mem_total_mb"] = round(total / 1024) + out["mem_used_mb"] = round((total - avail) / 1024) + out["mem_pct"] = round((total - avail) / total * 100, 1) if total else 0 + except Exception: + out["mem_total_mb"] = None + out["mem_used_mb"] = None + out["mem_pct"] = None + + return jsonify(out) + + +@app.route("/api/client_log", methods=["POST"]) +def api_client_log(): + """ + Приём логов/ошибок из браузера (front-end). + Пишет в client_errors.log рядом с routes.py. + """ + try: + data = request.get_json(force=True, silent=True) or {} + except Exception: + data = {} + + level = str(data.get("level") or "info").lower() + msg = str(data.get("msg") or "") + ctx = data.get("ctx") + ts = data.get("ts") + url = data.get("url") + ua = data.get("ua") + + try: + now = time.strftime("%Y-%m-%d %H:%M:%S") + line = f"[{now}] level={level} msg={msg} url={url} ua={ua} ctx={ctx} ts={ts}" + _append_client_log_line(line) + except Exception: + pass + + return jsonify({"ok": True}) + + +@app.route("/api/client_log_tail") +def api_client_log_tail(): + """Хвост client_errors.log для диагностики без SSH.""" + try: + n = request.args.get("n", "200") + try: + n = int(n) + except Exception: + n = 200 + n = max(1, min(2000, n)) + + path = os.path.join(os.path.dirname(__file__), "client_errors.log") + if not os.path.exists(path): + return jsonify({"ok": True, "exists": False, "lines": []}) + + with open(path, "r", encoding="utf-8", errors="replace") as f: + lines = f.readlines() + tail = [ln.rstrip("\n") for ln in lines[-n:]] + return jsonify({"ok": True, "exists": True, "lines": tail}) + except Exception as e: + return jsonify({"ok": False, "error": str(e)}), 500 + + +@app.route("/api/transponder", methods=["GET"]) +def api_transponder_get(): + """Настройки Class B. ownship тянется фронтом отдельно через /api/v1/ownship.""" + try: + cfg = normalize_transponder_config(load_transponder_config()) + return jsonify( + { + "ok": True, + "config": cfg, + "aistx_phy_available": is_aistx_phy_available(), + } + ) + except Exception as e: + return jsonify({"ok": False, "error": str(e)}), 500 + + +def _fetch_ownship_from_hub(): + """Best-effort: последний GPS-fix из ais_hub. Возвращает dict (возможно пустой).""" + try: + resp = urlopen(f"{AIS_HUB_URL}/api/v1/ownship", timeout=2.0) + raw = resp.read() + own = json.loads(raw.decode("utf-8", errors="ignore")) + if not isinstance(own, dict): + return {} + # Адаптируем имена полей к тем, что ожидает transponder (course/speed/...). + return { + "lat": own.get("lat"), + "lon": own.get("lon"), + "course": own.get("cog"), + "speed": own.get("sog"), + "heading": None, + "timestamp": own.get("ts"), + "satellites": own.get("sats"), + "fix_quality": own.get("fix_quality"), + } + except Exception: + return {} + + +@app.route("/api/transponder", methods=["POST"]) +def api_transponder_save(): + """Сохранить настройки транспондера в transponder_config.json.""" + data = request.get_json(force=True) or {} + base = load_transponder_config() + merged = merge_transponder_request(base, data) + saved = save_transponder_config(merged) + return jsonify({"ok": True, "config": saved}) + + +@app.route("/api/transponder/preview", methods=["POST"]) +def api_transponder_preview(): + """NRZI hex для типов 18/19/24 без отправки (отправка — 127.0.0.1:6010).""" + data = request.get_json(force=True) or {} + base = load_transponder_config() + cfg = merge_transponder_request(base, data) + own = _fetch_ownship_from_hub() + try: + prev = build_preview(cfg, own) + except RuntimeError as e: + return jsonify({"ok": False, "error": str(e)}), 503 + except Exception as e: + return jsonify({"ok": False, "error": str(e)}), 400 + prev.pop("dictionaries", None) + return jsonify({"ok": True, "preview": prev}) + + +@app.route("/api/transponder/send", methods=["POST"]) +def api_transponder_send(): + """NRZI по UDP на 127.0.0.1:6010 с заголовком канал+слот (как тест слота).""" + data = request.get_json(force=True) or {} + which = data.get("which") + if which not in ("18", "19", "24A", "24B", "broadcast"): + return jsonify( + {"ok": False, "error": "which must be 18, 19, 24A, 24B, broadcast"} + ), 400 + + cfg_body = {k: v for k, v in data.items() if k != "which"} + base = load_transponder_config() + cfg = merge_transponder_request(base, cfg_body) + own = _fetch_ownship_from_hub() + try: + result = send_transmission(cfg, own, which) + except RuntimeError as e: + return jsonify({"ok": False, "error": str(e)}), 503 + except ValueError as e: + return jsonify({"ok": False, "error": str(e)}), 400 + except Exception as e: + return jsonify({"ok": False, "error": str(e)}), 500 + return jsonify({"ok": True, "result": result}) + + +@app.route("/api/transponder/gpio_pulse", methods=["POST"]) +def api_transponder_gpio_pulse(): + """Один импульс TX (GPIO), без UDP. Настройки скрипта — из тела запроса или transponder_config.""" + data = request.get_json(force=True) or {} + base = load_transponder_config() + cfg = merge_transponder_request(base, data) + pulse = tx_gpio_pulse(cfg, after_udp=False) + if not pulse.get("ok"): + err = pulse.get("error") or pulse.get("stderr") or "GPIO pulse failed" + return jsonify({"ok": False, "error": err, "pulse": pulse}), 400 + return jsonify({"ok": True, "pulse": pulse}) + + +@app.route("/api/transponder/send_raw", methods=["POST"]) +def api_transponder_send_raw(): + """Сырой NRZI (hex) + канал + слот → UDP 127.0.0.1:6010.""" + data = request.get_json(force=True) or {} + hx = data.get("nrzi_hex") or data.get("hex") + if not isinstance(hx, str) or not hx.strip(): + return jsonify({"ok": False, "error": "nrzi_hex (hex строка) обязателен"}), 400 + channel = data.get("channel", "A") + try: + slot = int(data.get("slot", 0)) + except (TypeError, ValueError): + return jsonify({"ok": False, "error": "slot must be integer 0..2249"}), 400 + if channel not in ("A", "B"): + return jsonify({"ok": False, "error": "channel must be A or B"}), 400 + if slot < 0 or slot > 2249: + return jsonify({"ok": False, "error": "slot must be 0..2249"}), 400 + try: + body = parse_nrzi_hex(hx) + except ValueError as e: + return jsonify({"ok": False, "error": str(e)}), 400 + if not body: + return jsonify({"ok": False, "error": "пустой payload после hex"}), 400 + cfg_body = { + k: v + for k, v in data.items() + if k not in ("nrzi_hex", "hex", "channel", "slot") + } + base = load_transponder_config() + cfg = merge_transponder_request(base, cfg_body) + try: + result = send_raw_nrzi_packet(channel, slot, body, cfg=cfg) + except ValueError as e: + return jsonify({"ok": False, "error": str(e)}), 400 + except Exception as e: + return jsonify({"ok": False, "error": str(e)}), 500 + return jsonify({"ok": True, "result": result}) + + +# Аппаратно проверенная NRZI-последовательность для «Тест слота». +TEST_AIS_NRZI = bytes([ + 102, 102, 102, 254, 149, 61, 224, 94, 245, 171, 174, 169, + 74, 84, 87, 105, 51, 82, 202, 166, 141, 99, 170, 170, + 170, 253, 236, 63, 170, 170, 170, 170, +]) + + +@app.route("/api/send_test_slot", methods=["POST"]) +def api_send_test_slot(): + """Отправляет тестовую датаграмму AIS NRZI в указанный слот/канал (UDP 127.0.0.1:6010).""" + data = request.get_json(force=True) + if not data: + return jsonify({"ok": False, "error": "Empty payload"}), 400 + + channel = data.get("channel") + slot = data.get("slot") + + if channel not in ("A", "B"): + return jsonify({"ok": False, "error": "channel must be 'A' or 'B'"}), 400 + if not isinstance(slot, int) or slot < 0 or slot > 2249: + return jsonify({"ok": False, "error": "slot must be 0..2249"}), 400 + + dest = ("127.0.0.1", 6010) + datagram = build_slot_udp_payload(channel, slot, TEST_AIS_NRZI) + + try: + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + s.sendto(datagram, dest) + s.close() + return jsonify({"ok": True, "dest": f"{dest[0]}:{dest[1]}", "size": len(datagram)}) + except Exception as e: + return jsonify({"ok": False, "error": str(e)}), 500 + + +# ==================== Network API ==================== + +@app.route("/api/network", methods=["GET"]) +def api_network_get(): + """Returns current network configuration and live status.""" + with network_config_lock: + cfg = load_network_config() + live = get_current_network_info() + return jsonify({"config": cfg, "live": live}) + + +@app.route("/api/network", methods=["POST"]) +def api_network_save(): + """Saves network configuration (does NOT switch mode immediately).""" + data = request.get_json(force=True) + if not data: + return jsonify({"ok": False, "error": "Empty payload"}), 400 + + with network_config_lock: + cfg = load_network_config() + for key in NETWORK_DEFAULTS: + if key in data: + cfg[key] = data[key] + save_network_config(cfg) + return jsonify({"ok": True, "config": cfg}) + + +@app.route("/api/network/switch", methods=["POST"]) +def api_network_switch(): + """Switches network mode to AP or WiFi by running the shell script.""" + data = request.get_json(force=True) + target = data.get("mode") if data else None + if target not in ("ap", "wifi"): + return jsonify({"ok": False, "error": "Provide 'mode': 'ap' or 'wifi'"}), 400 + + with network_config_lock: + cfg = load_network_config() + for key in NETWORK_DEFAULTS: + if key in data and key != "mode": + cfg[key] = data[key] + save_network_config(cfg) + + result = switch_network_mode(target) + return jsonify(result) + + +AIS_MINI_CONF = "/ais-mini.conf" +AIS_MINI_SERVICE = "aisMini.service" + +AIS_HUB_CONF = "/opt/aishub/config/config.yaml" +AIS_HUB_SERVICE = "ais_hub.service" + + +@app.route("/api/config", methods=["GET"]) +def api_config_get(): + """Returns the content of the AIS-catcher Mini configuration file.""" + try: + with open(AIS_MINI_CONF, "r") as f: + text = f.read() + return jsonify({"ok": True, "text": text}) + except FileNotFoundError: + return jsonify({"ok": False, "error": f"File not found: {AIS_MINI_CONF}"}), 404 + except Exception as e: + return jsonify({"ok": False, "error": str(e)}), 500 + + +@app.route("/api/config", methods=["POST"]) +def api_config_save(): + """Saves the AIS-catcher Mini configuration file.""" + data = request.get_json(force=True) + text = data.get("text") if data else None + if text is None: + return jsonify({"ok": False, "error": "No 'text' field"}), 400 + try: + with open(AIS_MINI_CONF, "w") as f: + f.write(text) + return jsonify({"ok": True}) + except Exception as e: + return jsonify({"ok": False, "error": str(e)}), 500 + + +@app.route("/api/service/restart", methods=["POST"]) +def api_service_restart(): + """Restarts the aisMini.service via systemctl.""" + try: + result = subprocess.run( + ["systemctl", "restart", AIS_MINI_SERVICE], + capture_output=True, text=True, timeout=30, + ) + if result.returncode == 0: + return jsonify({"ok": True, "message": f"{AIS_MINI_SERVICE} restarted"}) + else: + return jsonify({"ok": False, "error": result.stderr.strip() or f"Exit code {result.returncode}"}), 500 + except subprocess.TimeoutExpired: + return jsonify({"ok": False, "error": "Restart timed out"}), 500 + except Exception as e: + return jsonify({"ok": False, "error": str(e)}), 500 + + +@app.route("/api/service/status", methods=["GET"]) +def api_service_status(): + """Returns the status of aisMini.service.""" + try: + result = subprocess.run( + ["systemctl", "is-active", AIS_MINI_SERVICE], + capture_output=True, text=True, timeout=5, + ) + state = result.stdout.strip() + return jsonify({"ok": True, "state": state}) + except Exception as e: + return jsonify({"ok": True, "state": "unknown", "error": str(e)}) + + +@app.route("/api/config/aishub", methods=["GET"]) +def api_aishub_config_get(): + """Returns the content of the ais_hub configuration YAML.""" + try: + with open(AIS_HUB_CONF, "r", encoding="utf-8", errors="replace") as f: + text = f.read() + return jsonify({"ok": True, "text": text}) + except FileNotFoundError: + return jsonify({"ok": False, "error": f"File not found: {AIS_HUB_CONF}"}), 404 + except Exception as e: + return jsonify({"ok": False, "error": str(e)}), 500 + + +@app.route("/api/config/aishub", methods=["POST"]) +def api_aishub_config_save(): + """Saves the ais_hub configuration YAML.""" + data = request.get_json(force=True) + text = data.get("text") if data else None + if text is None: + return jsonify({"ok": False, "error": "No 'text' field"}), 400 + try: + with open(AIS_HUB_CONF, "w", encoding="utf-8") as f: + f.write(text) + return jsonify({"ok": True}) + except Exception as e: + return jsonify({"ok": False, "error": str(e)}), 500 + + +@app.route("/api/service/aishub/status", methods=["GET"]) +def api_aishub_service_status(): + """Returns the status of ais_hub.service.""" + try: + result = subprocess.run( + ["systemctl", "is-active", AIS_HUB_SERVICE], + capture_output=True, text=True, timeout=5, + ) + state = result.stdout.strip() + return jsonify({"ok": True, "state": state}) + except Exception as e: + return jsonify({"ok": True, "state": "unknown", "error": str(e)}) + + +@app.route("/api/service/aishub/restart", methods=["POST"]) +def api_aishub_service_restart(): + """Restarts ais_hub.service via systemctl.""" + try: + result = subprocess.run( + ["systemctl", "restart", AIS_HUB_SERVICE], + capture_output=True, text=True, timeout=30, + ) + if result.returncode == 0: + return jsonify({"ok": True, "message": f"{AIS_HUB_SERVICE} restarted"}) + else: + return jsonify({"ok": False, "error": result.stderr.strip() or f"Exit code {result.returncode}"}), 500 + except subprocess.TimeoutExpired: + return jsonify({"ok": False, "error": "Restart timed out"}), 500 + except Exception as e: + return jsonify({"ok": False, "error": str(e)}), 500 + + +@app.route("/api/network/scan", methods=["GET"]) +def api_network_scan(): + """Scans for available WiFi networks (Linux/iw only).""" + cfg = load_network_config() + iface = cfg.get("iface", "wlan0") + try: + out = subprocess.check_output( + ["iw", "dev", iface, "scan", "ap-force"], + timeout=15, stderr=subprocess.DEVNULL, + ).decode() + networks = [] + current = {} + for line in out.splitlines(): + line = line.strip() + if line.startswith("BSS "): + if current.get("ssid"): + networks.append(current) + current = {"bssid": line.split()[1].rstrip("("), "ssid": "", "signal": None} + elif line.startswith("SSID:"): + current["ssid"] = line.split(":", 1)[1].strip() + elif line.startswith("signal:"): + try: + current["signal"] = float(line.split(":")[1].strip().split()[0]) + except (ValueError, IndexError): + pass + if current.get("ssid"): + networks.append(current) + seen = set() + unique = [] + for n in networks: + if n["ssid"] not in seen: + seen.add(n["ssid"]) + unique.append(n) + unique.sort(key=lambda x: x.get("signal") or -999, reverse=True) + return jsonify({"ok": True, "networks": unique}) + except subprocess.TimeoutExpired: + return jsonify({"ok": False, "error": "Scan timed out"}) + except Exception as e: + return jsonify({"ok": False, "error": str(e), "networks": []}) + + +# ==================== Certificate downloads ==================== + +@app.route("/ca.pem") +def download_ca(): + """Отдаёт корневой CA-сертификат для установки на клиентские устройства.""" + ca_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "ca.pem") + if not os.path.exists(ca_path): + return "CA certificate not generated", 404 + return send_file(ca_path, mimetype="application/x-pem-file", + as_attachment=True, download_name="AISMap_CA.pem") + + +@app.route("/ca.crt") +def download_ca_crt(): + """Тот же CA, но с расширением .crt — Android открывает установщик автоматически.""" + ca_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "ca.pem") + if not os.path.exists(ca_path): + return "CA certificate not generated", 404 + return send_file(ca_path, mimetype="application/x-x509-ca-cert", + as_attachment=True, download_name="AISMap_CA.crt") + + +# ==================== Static files with caching ==================== + +IMMUTABLE_YEAR = "public, max-age=31536000, immutable" +STATIC_MONTH = "public, max-age=2592000" +SHORT_REVALIDATE = "public, max-age=60, must-revalidate" + + +def _etag_for_path(path): + try: + st = os.stat(path) + h = hashlib.md5(f"{st.st_mtime_ns}:{st.st_size}".encode("ascii")).hexdigest() + return f'W/"{h}"' + except Exception: + return None + + +def _serve_with_cache(directory, filename, cache_control): + """send_from_directory + заголовок Cache-Control + ETag/304.""" + abs_path = os.path.join(directory, filename) + etag = _etag_for_path(abs_path) + inm = request.headers.get("If-None-Match") + if etag and inm and inm == etag: + resp = Response(status=304) + resp.headers["ETag"] = etag + resp.headers["Cache-Control"] = cache_control + return resp + resp = send_from_directory(directory, filename) + resp.headers["Cache-Control"] = cache_control + if etag: + resp.headers["ETag"] = etag + return resp + + +@app.route("/svg/") +def serve_svg(filename): + """SVG-иконки — редко меняются, долгий кеш.""" + return _serve_with_cache("SVG", filename, STATIC_MONTH) + + +@app.route("/static/leaflet/") +def serve_leaflet(filename): + """Leaflet (версионирован по содержимому) — immutable.""" + return _serve_with_cache("static/leaflet", filename, IMMUTABLE_YEAR) + + +@app.route("/static/xterm/") +def serve_xterm(filename): + """xterm.js — immutable.""" + return _serve_with_cache("static/xterm", filename, IMMUTABLE_YEAR) + + +@app.route("/static/js/") +def serve_static_js(filename): + """app.js, модули — SWR: всегда проверяем обновление, но отдаём из кеша если не изменилось.""" + return _serve_with_cache("static/js", filename, SHORT_REVALIDATE) + + +@app.route("/static/css/") +def serve_static_css(filename): + return _serve_with_cache("static/css", filename, SHORT_REVALIDATE) + + +@app.route("/sw.js") +def serve_sw(): + """Service Worker должен быть в корне для scope '/'.""" + path = os.path.join(os.path.dirname(__file__), "static", "sw.js") + if not os.path.exists(path): + return "sw.js not found", 404 + with open(path, "r", encoding="utf-8") as f: + body = f.read() + resp = Response(body, mimetype="application/javascript") + resp.headers["Cache-Control"] = "no-store" + resp.headers["Service-Worker-Allowed"] = "/" + return resp + + +VECTOR_TILE_URL = os.environ.get("AISMAP_VECTOR_TILE_URL", "http://127.0.0.1:8080").rstrip("/") +VECTOR_TILE_SERVICE = os.environ.get("AISMAP_VECTOR_TILE_SERVICE", "planet_small_z14").strip().strip("/") + + +@app.route("/vtiles///.pbf") +def proxy_vector_tile(z, x, y): + """Проксирует PBF-тайлы с локального векторного тайлсервера. immutable: тайл не меняется.""" + try: + candidates = [ + f"{VECTOR_TILE_URL}/services/{VECTOR_TILE_SERVICE}/tiles/{z}/{x}/{y}.pbf", + f"{VECTOR_TILE_URL}/services/{VECTOR_TILE_SERVICE}/{z}/{x}/{y}.pbf", + f"{VECTOR_TILE_URL}/data/{VECTOR_TILE_SERVICE}/{z}/{x}/{y}.pbf", + f"{VECTOR_TILE_URL}/{VECTOR_TILE_SERVICE}/{z}/{x}/{y}.pbf", + ] + + last_err = None + for url in candidates: + try: + resp = urlopen(url, timeout=5) + data = resp.read() + code = getattr(resp, "status", 200) or 200 + headers = { + "Content-Type": resp.headers.get("Content-Type", "application/x-protobuf"), + "Cache-Control": IMMUTABLE_YEAR, + "Access-Control-Allow-Origin": "*", + "X-AISMap-Upstream": url, + } + ce = resp.headers.get("Content-Encoding") + if ce: + headers["Content-Encoding"] = ce + return data, code, headers + except HTTPError as e: + last_err = e + if getattr(e, "code", None) != 404: + raise + except URLError as e: + last_err = e + break + + if isinstance(last_err, HTTPError) and getattr(last_err, "code", None) == 404: + return "Vector tile not found", 404 + return "Vector tile upstream unavailable", 503 + except (URLError, HTTPError): + return "Vector tile upstream error", 503 + + +@app.route("/tiles///.png") +def serve_tile(z, x, y): + """Отдаёт растровые тайлы из static/tiles с вечным кешем (immutable).""" + max_tile = 2 ** z + if x < 0 or x >= max_tile or y < 0 or y >= max_tile: + return "Invalid tile coordinates", 404 + + rel_dir = os.path.join("static", "tiles", str(z), str(x)) + tile_path = os.path.join(rel_dir, f"{y}.png") + + if not os.path.exists(tile_path): + return "Tile not found", 404 + + etag = _etag_for_path(tile_path) + inm = request.headers.get("If-None-Match") + if etag and inm and inm == etag: + resp = Response(status=304) + resp.headers["ETag"] = etag + resp.headers["Cache-Control"] = IMMUTABLE_YEAR + return resp + + resp = send_from_directory(rel_dir, f"{y}.png") + resp.headers["Cache-Control"] = IMMUTABLE_YEAR + if etag: + resp.headers["ETag"] = etag + return resp diff --git a/scripts/aismap-network.service b/scripts/aismap-network.service new file mode 100644 index 0000000..0ec4f4d --- /dev/null +++ b/scripts/aismap-network.service @@ -0,0 +1,14 @@ +[Unit] +Description=AIS Map Network Init (AP/WiFi with rollback) +After=network-pre.target +Before=network.target hostapd.service +Wants=network-pre.target + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=/opt/aismap/scripts/network_init.sh +TimeoutStartSec=60 + +[Install] +WantedBy=multi-user.target diff --git a/scripts/install.sh b/scripts/install.sh new file mode 100644 index 0000000..bf4a1a4 --- /dev/null +++ b/scripts/install.sh @@ -0,0 +1,80 @@ +#!/bin/bash +# +# Install AIS Map network scripts and systemd service. +# Run as root on the target device. +# +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +INSTALL_DIR="/opt/aismap/scripts" +CONFIG_DIR="/etc/aismap" +CONFIG_FILE="$CONFIG_DIR/network.json" +HOSTAPD_CONF="/etc/hostapd/hostapd.conf" + +echo "=== AIS Map network scripts installer ===" + +mkdir -p "$INSTALL_DIR" +mkdir -p "$CONFIG_DIR" + +echo "[1] Copying scripts to $INSTALL_DIR ..." +cp "$SCRIPT_DIR/to_wifi.sh" "$INSTALL_DIR/" +cp "$SCRIPT_DIR/to_ap.sh" "$INSTALL_DIR/" +cp "$SCRIPT_DIR/network_init.sh" "$INSTALL_DIR/" +chmod +x "$INSTALL_DIR"/*.sh + +echo "[2] Creating default config (if not exists) ..." +if [ ! -f "$CONFIG_FILE" ]; then + # Read current AP settings from hostapd.conf + AP_SSID="" + AP_PSK="" + AP_IFACE="wlan0" + if [ -f "$HOSTAPD_CONF" ]; then + AP_SSID=$(grep -E '^ssid=' "$HOSTAPD_CONF" | head -1 | cut -d= -f2 || echo "") + AP_PSK=$(grep -E '^wpa_passphrase=' "$HOSTAPD_CONF" | head -1 | cut -d= -f2 || echo "") + AP_IFACE=$(grep -E '^interface=' "$HOSTAPD_CONF" | head -1 | cut -d= -f2 || echo "wlan0") + echo " Read from hostapd.conf: SSID=$AP_SSID, iface=$AP_IFACE" + fi + + cat > "$CONFIG_FILE" </dev/null || true +echo " hostapd auto-start disabled (we start it from to_ap.sh)" + +echo "" +echo "=== Installation complete ===" +echo "" +echo "Usage:" +echo " - Web UI: open Settings tab -> 'Сеть / Режим работы'" +echo " - Manual switch to WiFi: bash $INSTALL_DIR/to_wifi.sh" +echo " - Manual switch to AP: bash $INSTALL_DIR/to_ap.sh" +echo " - Config file: $CONFIG_FILE" +echo " - Boot init logs: /var/log/aismap_network_init.log" +echo " - Hostapd backup: ${HOSTAPD_CONF}.orig (created on first AP switch)" +echo "" +echo "On next reboot, network_init.sh will run automatically." +echo "If WiFi fails, it will roll back to AP mode." diff --git a/scripts/network_init.sh b/scripts/network_init.sh new file mode 100644 index 0000000..50d64d6 --- /dev/null +++ b/scripts/network_init.sh @@ -0,0 +1,75 @@ +#!/bin/bash +# +# AIS Map network init — runs at boot. +# Reads /etc/aismap/network.json, tries the configured mode. +# If mode=wifi and connection fails — rolls back to AP. +# + +set -uo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +CONFIG="/etc/aismap/network.json" +LOG="/var/log/aismap_network_init.log" +exec >"$LOG" 2>&1 + +echo "=== $(date) network_init start ===" + +if [ ! -f "$CONFIG" ]; then + echo "Config not found ($CONFIG), defaulting to AP mode" + bash "$SCRIPT_DIR/to_ap.sh" + echo "=== $(date) network_init done (default AP) ===" + exit 0 +fi + +MODE=$(python3 -c "import json; print(json.load(open('$CONFIG')).get('mode','ap'))" 2>/dev/null || echo "ap") +echo "Configured mode: $MODE" + +if [ "$MODE" = "wifi" ]; then + echo "--- Attempting WiFi connection ---" + bash "$SCRIPT_DIR/to_wifi.sh" + WIFI_EXIT=$? + + if [ $WIFI_EXIT -ne 0 ]; then + echo "to_wifi.sh failed (exit $WIFI_EXIT) — rolling back to AP" + bash "$SCRIPT_DIR/to_ap.sh" + echo "=== $(date) network_init done (rollback to AP) ===" + exit 0 + fi + + GW=$(python3 -c "import json; print(json.load(open('$CONFIG')).get('wifi_gw',''))" 2>/dev/null || echo "") + IFACE=$(python3 -c "import json; print(json.load(open('$CONFIG')).get('iface','wlan0'))" 2>/dev/null || echo "wlan0") + + echo "Verifying WiFi connectivity (gateway: $GW) ..." + CONNECTED=0 + for ATTEMPT in 1 2 3; do + sleep 3 + if [ -n "$GW" ] && ping -c 2 -W 3 "$GW" >/dev/null 2>&1; then + echo "Attempt $ATTEMPT: gateway reachable" + CONNECTED=1 + break + fi + # Also check wpa_supplicant state + if wpa_cli -i "$IFACE" status 2>/dev/null | grep -q "wpa_state=COMPLETED"; then + echo "Attempt $ATTEMPT: wpa_supplicant COMPLETED" + CONNECTED=1 + break + fi + echo "Attempt $ATTEMPT: not connected yet..." + done + + if [ $CONNECTED -eq 1 ]; then + echo "WiFi connection confirmed" + echo "=== $(date) network_init done (wifi) ===" + exit 0 + else + echo "WiFi NOT reachable after 3 attempts — rolling back to AP" + bash "$SCRIPT_DIR/to_ap.sh" + echo "=== $(date) network_init done (rollback to AP) ===" + exit 0 + fi +else + echo "--- Starting AP mode ---" + bash "$SCRIPT_DIR/to_ap.sh" + echo "=== $(date) network_init done (ap) ===" + exit 0 +fi diff --git a/scripts/pulse_once.py b/scripts/pulse_once.py new file mode 100644 index 0000000..564106f --- /dev/null +++ b/scripts/pulse_once.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +"""Короткий импульс на GPIO (Linux + gpiod). Пример для Orange Pi: скопируйте на устройство и укажите путь в настройках транспондера.""" +import os +import time + +try: + import gpiod +except ImportError: + raise SystemExit("Нужен пакет gpiod (python3-libgpiod)") from None + +CHIP = os.environ.get("AIS_TX_GPIO_CHIP", "/dev/gpiochip1") +LINE = int(os.environ.get("AIS_TX_GPIO_LINE", "0")) +PULSE_US = int(os.environ.get("AIS_TX_PULSE_US", "1000")) + +with gpiod.request_lines( + CHIP, + consumer="ais-tx-pulse", + config={ + LINE: gpiod.LineSettings( + direction=gpiod.line.Direction.OUTPUT, + output_value=gpiod.line.Value.INACTIVE, + ) + }, +) as req: + req.set_value(LINE, gpiod.line.Value.ACTIVE) + time.sleep(PULSE_US / 1_000_000.0) + req.set_value(LINE, gpiod.line.Value.INACTIVE) diff --git a/scripts/to_ap.sh b/scripts/to_ap.sh new file mode 100644 index 0000000..958bb3c --- /dev/null +++ b/scripts/to_ap.sh @@ -0,0 +1,67 @@ +#!/bin/bash +set -euo pipefail + +CONFIG="/etc/aismap/network.json" +HOSTAPD_CONF="/etc/hostapd/hostapd.conf" +LOG="/var/log/aismap_to_ap.log" +exec >"$LOG" 2>&1 + +echo "=== $(date) start to_ap ===" + +IFACE="wlan0" +AP_IP="192.168.4.1/24" +AP_SSID="" +AP_PSK="" + +if [ -f "$CONFIG" ]; then + IFACE=$(python3 -c "import json; print(json.load(open('$CONFIG')).get('iface','wlan0'))" 2>/dev/null || echo "wlan0") + AP_IP=$(python3 -c "import json; print(json.load(open('$CONFIG')).get('ap_ip','192.168.4.1/24'))" 2>/dev/null || echo "192.168.4.1/24") + AP_SSID=$(python3 -c "import json; print(json.load(open('$CONFIG')).get('ap_ssid',''))" 2>/dev/null || echo "") + AP_PSK=$(python3 -c "import json; print(json.load(open('$CONFIG')).get('ap_psk',''))" 2>/dev/null || echo "") +fi + +echo "[1] stop wifi client" +killall wpa_supplicant 2>/dev/null || true +sleep 1 + +echo "[2] reset iface $IFACE" +ip addr flush dev "$IFACE" 2>/dev/null || true +ip route del default 2>/dev/null || true +ip link set "$IFACE" down 2>/dev/null || true +sleep 1 +ip link set "$IFACE" up +sleep 2 + +echo "[3] restore AP IP: $AP_IP" +ip addr add "$AP_IP" dev "$IFACE" + +echo "[4] update hostapd.conf (if new SSID/PSK provided)" +if [ -n "$AP_SSID" ] && [ -f "$HOSTAPD_CONF" ]; then + # Back up the original before first modification + if [ ! -f "${HOSTAPD_CONF}.orig" ]; then + cp "$HOSTAPD_CONF" "${HOSTAPD_CONF}.orig" + echo " backed up original to ${HOSTAPD_CONF}.orig" + fi + sed -i "s/^ssid=.*/ssid=${AP_SSID}/" "$HOSTAPD_CONF" + echo " ssid -> $AP_SSID" + if [ -n "$AP_PSK" ]; then + sed -i "s/^wpa_passphrase=.*/wpa_passphrase=${AP_PSK}/" "$HOSTAPD_CONF" + echo " wpa_passphrase -> (updated)" + fi + # Keep interface in sync + sed -i "s/^interface=.*/interface=${IFACE}/" "$HOSTAPD_CONF" +else + echo " no SSID in config or hostapd.conf missing — using existing hostapd.conf" +fi + +echo "[5] start hostapd" +hostapd -B "$HOSTAPD_CONF" + +echo "[6] restart dnsmasq (if used)" +systemctl restart dnsmasq 2>/dev/null || true + +echo "[7] status" +ip addr show dev "$IFACE" +iw dev 2>/dev/null || true + +echo "=== $(date) done to_ap ===" diff --git a/scripts/to_wifi.sh b/scripts/to_wifi.sh new file mode 100644 index 0000000..e69d567 --- /dev/null +++ b/scripts/to_wifi.sh @@ -0,0 +1,86 @@ +#!/bin/bash +set -euo pipefail + +CONFIG="/etc/aismap/network.json" +LOG="/var/log/aismap_to_wifi.log" +exec >"$LOG" 2>&1 + +echo "=== $(date) start to_wifi ===" + +if [ ! -f "$CONFIG" ]; then + echo "ERROR: config not found: $CONFIG" + exit 1 +fi + +SSID=$(python3 -c "import json; print(json.load(open('$CONFIG'))['wifi_ssid'])") +PSK=$(python3 -c "import json; print(json.load(open('$CONFIG'))['wifi_psk'])") +WIFI_IP=$(python3 -c "import json; print(json.load(open('$CONFIG'))['wifi_ip'])") +GW=$(python3 -c "import json; print(json.load(open('$CONFIG')).get('wifi_gw',''))") +DNS=$(python3 -c "import json; print(json.load(open('$CONFIG')).get('wifi_dns','8.8.8.8'))") +IFACE=$(python3 -c "import json; print(json.load(open('$CONFIG')).get('iface','wlan0'))") + +if [ -z "$SSID" ]; then + echo "ERROR: wifi_ssid is empty" + exit 1 +fi + +echo "[1] stop AP services" +killall hostapd 2>/dev/null || true +killall wpa_supplicant 2>/dev/null || true +sleep 1 + +echo "[2] reset iface $IFACE" +ip link set "$IFACE" down 2>/dev/null || true +sleep 1 +ip link set "$IFACE" up +sleep 2 + +echo "[3] scan for SSID: $SSID" +iw dev "$IFACE" scan 2>/dev/null | grep -F "SSID: $SSID" || echo "WARN: SSID not found in scan, trying anyway" + +echo "[4] build wpa config" +wpa_passphrase "$SSID" "$PSK" > /tmp/aismap_wifi.conf + +echo "[5] connect to wifi" +wpa_supplicant -B -i "$IFACE" -c /tmp/aismap_wifi.conf +sleep 5 + +echo "[6] flush old IPs" +ip addr flush dev "$IFACE" + +echo "[7] set static IP: $WIFI_IP" +ip addr add "$WIFI_IP" dev "$IFACE" + +echo "[8] set default route" +ip route del default 2>/dev/null || true +if [ -n "$GW" ]; then + ip route add default via "${GW}" dev "$IFACE" +fi + +echo "[9] set DNS" +if [ -n "$DNS" ]; then + echo "nameserver $DNS" > /etc/resolv.conf +fi + +echo "[10] verify connectivity" +sleep 2 +CURRENT_IP=$(ip -4 addr show "$IFACE" | grep -oP 'inet \K[0-9./]+' || echo "none") +echo "IP on $IFACE: $CURRENT_IP" + +if [ -n "$GW" ]; then + if ping -c 2 -W 3 "${GW}" >/dev/null 2>&1; then + echo "Gateway $GW reachable — WiFi OK" + else + echo "WARN: gateway $GW unreachable" + fi +else + echo "No gateway configured, skipping ping check" +fi + +echo "[11] wpa_supplicant status" +wpa_cli -i "$IFACE" status 2>/dev/null || true + +ip addr show dev "$IFACE" +iw dev "$IFACE" link 2>/dev/null || true + +echo "=== $(date) done to_wifi ===" diff --git a/ssl_utils.py b/ssl_utils.py new file mode 100644 index 0000000..a7e2755 --- /dev/null +++ b/ssl_utils.py @@ -0,0 +1,148 @@ +import os +import ssl +import socket +import datetime + + +def _get_local_ips(): + """Возвращает список локальных IP-адресов машины.""" + ips = [] + try: + for info in socket.getaddrinfo(socket.gethostname(), None, socket.AF_INET): + ip = info[4][0] + if ip not in ips: + ips.append(ip) + except Exception: + pass + return ips + + +def get_ssl_context(): + """ + Создаёт собственный мини-CA и подписывает серверный сертификат. + CA-сертификат можно установить на телефон один раз — и предупреждения исчезнут. + Файлы: ca.pem (корневой, для установки на клиенты), cert.pem + key.pem (сервер). + """ + cert_dir = os.path.dirname(os.path.abspath(__file__)) + ca_cert_file = os.path.join(cert_dir, "ca.pem") + ca_key_file = os.path.join(cert_dir, "ca_key.pem") + cert_file = os.path.join(cert_dir, "cert.pem") + key_file = os.path.join(cert_dir, "key.pem") + + need_regen = not all(os.path.exists(f) for f in [ca_cert_file, ca_key_file, cert_file, key_file]) + + if need_regen: + try: + from cryptography import x509 + from cryptography.x509.oid import NameOID, ExtendedKeyUsageOID + from cryptography.hazmat.primitives import hashes, serialization + from cryptography.hazmat.primitives.asymmetric import rsa + import ipaddress + except ImportError: + print("[SSL] Библиотека cryptography не установлена.") + print("[SSL] Установите: pip install cryptography") + print("[SSL] Без HTTPS GPS телефона работать не будет.") + return None + + print("[SSL] Генерация корневого CA и серверного сертификата...") + + # === 1. Корневой CA === + ca_key = rsa.generate_private_key(public_exponent=65537, key_size=2048) + ca_name = x509.Name([ + x509.NameAttribute(NameOID.ORGANIZATION_NAME, "AIS Map CA"), + x509.NameAttribute(NameOID.COMMON_NAME, "AIS Map Root CA"), + ]) + ca_cert = ( + x509.CertificateBuilder() + .subject_name(ca_name) + .issuer_name(ca_name) + .public_key(ca_key.public_key()) + .serial_number(x509.random_serial_number()) + .not_valid_before(datetime.datetime.utcnow()) + .not_valid_after(datetime.datetime.utcnow() + datetime.timedelta(days=3650)) + .add_extension(x509.BasicConstraints(ca=True, path_length=0), critical=True) + .add_extension( + x509.KeyUsage( + digital_signature=True, key_cert_sign=True, crl_sign=True, + content_commitment=False, key_encipherment=False, + data_encipherment=False, key_agreement=False, + encipher_only=False, decipher_only=False, + ), + critical=True, + ) + .sign(ca_key, hashes.SHA256()) + ) + + with open(ca_cert_file, "wb") as f: + f.write(ca_cert.public_bytes(serialization.Encoding.PEM)) + with open(ca_key_file, "wb") as f: + f.write(ca_key.private_bytes( + serialization.Encoding.PEM, + serialization.PrivateFormat.TraditionalOpenSSL, + serialization.NoEncryption(), + )) + print(f"[SSL] CA сертификат: {ca_cert_file}") + + # === 2. Серверный сертификат, подписанный CA === + srv_key = rsa.generate_private_key(public_exponent=65537, key_size=2048) + srv_name = x509.Name([ + x509.NameAttribute(NameOID.ORGANIZATION_NAME, "AIS Map"), + x509.NameAttribute(NameOID.COMMON_NAME, "aismap.local"), + ]) + + san_entries = [x509.DNSName("aismap.local"), x509.DNSName("localhost")] + for iface_ip in _get_local_ips(): + try: + san_entries.append(x509.IPAddress(ipaddress.IPv4Address(iface_ip))) + except Exception: + pass + san_entries.append(x509.IPAddress(ipaddress.IPv4Address("127.0.0.1"))) + + srv_cert = ( + x509.CertificateBuilder() + .subject_name(srv_name) + .issuer_name(ca_name) + .public_key(srv_key.public_key()) + .serial_number(x509.random_serial_number()) + .not_valid_before(datetime.datetime.utcnow()) + .not_valid_after(datetime.datetime.utcnow() + datetime.timedelta(days=3650)) + .add_extension(x509.BasicConstraints(ca=False, path_length=None), critical=True) + .add_extension(x509.SubjectAlternativeName(san_entries), critical=False) + .add_extension( + x509.ExtendedKeyUsage([ExtendedKeyUsageOID.SERVER_AUTH]), + critical=False, + ) + .sign(ca_key, hashes.SHA256()) + ) + + with open(cert_file, "wb") as f: + f.write(srv_cert.public_bytes(serialization.Encoding.PEM)) + with open(key_file, "wb") as f: + f.write(srv_key.private_bytes( + serialization.Encoding.PEM, + serialization.PrivateFormat.TraditionalOpenSSL, + serialization.NoEncryption(), + )) + print(f"[SSL] Серверный сертификат: {cert_file}") + + ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + ctx.load_cert_chain(cert_file, key_file) + return ctx + + +def run_http_redirect(https_port): + """Простой HTTP-сервер на порту 80, который редиректит на HTTPS.""" + from flask import Flask, redirect, request as flask_request + redir_app = Flask("redirect") + + @redir_app.route("/", defaults={"path": ""}) + @redir_app.route("/") + def redir(path): + host = flask_request.host.split(":")[0] + port_suffix = f":{https_port}" if https_port != 443 else "" + return redirect(f"https://{host}{port_suffix}/{path}", code=301) + + try: + redir_app.run(host="0.0.0.0", port=80, threaded=True) + except Exception as e: + print(f"[HTTP] Не удалось запустить редирект на порту 80: {e}") diff --git a/state.py b/state.py new file mode 100644 index 0000000..d87152b --- /dev/null +++ b/state.py @@ -0,0 +1,49 @@ +import threading + +# Все структуры AIS-данных (vessels/base_stations/buoys/stats/slots/...) вынесены в +# централизованный процесс ais_hub (localhost:8081). Здесь остаётся только функция +# измерения загрузки CPU для эндпоинта /api/sysinfo — ais_hub системные метрики не отдаёт. + +_cpu_prev_jiffies = None +_cpu_jiffy_lock = threading.Lock() + + +def _read_cpu_aggregated_jiffies(): + """Суммарные jiffies по всем ядрам: первая строка cpu в /proc/stat.""" + with open("/proc/stat", "r") as f: + line = f.readline() + parts = line.split() + if not parts or parts[0] != "cpu": + return None + nums = [int(x) for x in parts[1:]] + idle = nums[3] + nums[4] # idle + iowait + total = sum(nums) + return total, idle + + +def get_cpu_usage_percent(): + """ + Загрузка CPU в % (0–100), как в htop: разница двух замеров /proc/stat. + Первый запрос после старта может вернуть None — ещё нет предыдущего замера. + """ + global _cpu_prev_jiffies + try: + cur = _read_cpu_aggregated_jiffies() + if cur is None: + return None + total, idle = cur + with _cpu_jiffy_lock: + prev = _cpu_prev_jiffies + _cpu_prev_jiffies = (total, idle) + if prev is None: + return None + p_total, p_idle = prev + dt = total - p_total + di = idle - p_idle + if dt <= 0: + return None + busy = dt - di + pct = max(0.0, min(100.0, 100.0 * busy / dt)) + return round(pct, 1) + except Exception: + return None diff --git a/static/css/style.css b/static/css/style.css new file mode 100644 index 0000000..8f6952d --- /dev/null +++ b/static/css/style.css @@ -0,0 +1,1359 @@ +*{box-sizing:border-box;margin:0;padding:0} +html,body{height:100%;overflow-x:hidden;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;background:#1a1a2e;color:#e0e0e0;overscroll-behavior-y:contain} + +/* ===== Navbar ===== */ +#navbar{display:flex;align-items:center;height:44px;background:#16213e;border-bottom:1px solid #0f3460;padding:0 12px;z-index:2000;position:relative} +.nav-brand{font-size:15px;font-weight:700;color:#d2ff1a;margin-right:20px;white-space:nowrap} +.nav-tabs{display:flex;gap:2px} +.nav-tab{padding:8px 16px;color:#8899aa;font-size:13px;cursor:pointer;border-radius:4px 4px 0 0;transition:background .15s,color .15s;text-decoration:none;user-select:none} +.nav-tab:hover{color:#ccc;background:rgba(255,255,255,.05)} +.nav-tab.active{color:#d2ff1a;background:#1a1a2e;font-weight:600} +.hamburger{display:none;background:none;border:none;color:#d2ff1a;font-size:22px;cursor:pointer;padding:4px 8px} + +/* Connection banner (top-right) */ +.conn-banner{ + /* Make it global, centered, high-contrast (works on light maps too) */ + position:fixed; + left:50%; + top:54px; /* below navbar */ + transform:translateX(-50%); + z-index:2500; + padding:7px 12px; + border-radius:999px; + font-size:12px; + font-weight:800; + white-space:nowrap; + max-width:80vw; + overflow:hidden; + text-overflow:ellipsis; + border:1px solid transparent; + box-shadow:0 6px 18px rgba(0,0,0,.45); + backdrop-filter: blur(6px); +} +.conn-banner--offline{background:rgba(248,81,73,.18);border-color:rgba(248,81,73,.55);color:#ffb4b0} +.conn-banner--online{background:rgba(63,185,80,.14);border-color:rgba(63,185,80,.5);color:#7ee787} +@media(max-width:600px){ + .nav-tabs{display:none;position:absolute;top:44px;left:0;right:0;background:#16213e;flex-direction:column;border-bottom:1px solid #0f3460;z-index:2001} + .nav-tabs.open{display:flex} + .nav-tab{padding:12px 20px;border-radius:0} + .hamburger{display:block} +} + +/* ===== Tab pages ===== */ +.tab-page{display:none;height:calc(100vh - 44px);overflow:hidden;position:relative} +@supports (height: 100dvh){ + .tab-page{height:calc(100dvh - 44px)} +} +.tab-page.active{display:block} + +/* ===== Map page ===== */ +#map{width:100%;height:100%;background:#0e1a2b} +#status{position:absolute;z-index:1000;top:8px;left:8px;background:rgba(22,33,62,.92);padding:6px 12px;border-radius:4px;font-size:13px;color:#d2ff1a;box-shadow:0 2px 6px rgba(0,0,0,.4)} +#sidebar{position:absolute;z-index:1000;top:8px;right:8px;width:280px;max-height:calc(100% - 16px);background:rgba(22,33,62,.95);padding:10px;border-radius:6px;box-shadow:0 2px 8px rgba(0,0,0,.4);font-size:12px;display:flex;flex-direction:column;overflow:hidden} +#sidebar h3{margin:0 0 8px;font-size:13px;border-bottom:1px solid #0f3460;padding-bottom:5px;color:#8899aa;flex-shrink:0} +.vessel-sidebar-heading{font-size:15px;margin:0 0 8px} +.vessel-count-paren{font-weight:600;color:#c9d1d9} +#vessel-count-suffix{color:#8b949e;font-weight:400} +.vessel-list-toolbar{margin-bottom:8px;flex-shrink:0} +.vessel-search-input{width:100%;box-sizing:border-box;padding:6px 8px;margin-bottom:6px;border:1px solid #30363d;border-radius:4px;background:#0d1117;color:#c9d1d9;font-size:12px} +.vessel-search-input::placeholder{color:#6e7681} +.vessel-search-input:focus{outline:none;border-color:#4fc3f7} +.vessel-toolbar-row{display:flex;flex-wrap:wrap;gap:6px 10px;align-items:flex-end} +.vessel-toolbar-label{font-size:10px;color:#8b949e;display:flex;flex-direction:column;gap:2px;min-width:0} +.vessel-toolbar-select{max-width:140px;padding:4px 6px;border-radius:4px;border:1px solid #30363d;background:#161b22;color:#c9d1d9;font-size:11px} +#vessel-list{flex:1;overflow-y:auto;min-height:0} +#range-filter{flex-shrink:0} +.leaflet-control-zoom{display:none} +.leaflet-control-attribution{display:none} +.leaflet-control-layers{background:rgba(22,33,62,.95)!important;border:1px solid #0f3460!important;border-radius:6px!important;color:#e0e0e0!important;box-shadow:0 2px 8px rgba(0,0,0,.4)!important} +.leaflet-control-layers label{color:#ccd} +.leaflet-control-layers-toggle{width:30px!important;height:30px!important;background-color:rgba(22,33,62,.92)!important;border:1px solid #0f3460!important;border-radius:4px!important} +.leaflet-control-layers-expanded{padding:8px 12px 6px!important} +.leaflet-control-layers-base label{margin-bottom:2px;display:flex;align-items:center;gap:6px;font-size:13px;cursor:pointer} + +/* Compass UI (inside OwnShip panel) */ +.os-compass-row{display:flex;align-items:center;justify-content:space-between;gap:10px} +.os-compass{display:inline-flex;align-items:center;gap:8px} +.compass-toggle{ + width:28px;height:28px; + border-radius:8px; + border:1px solid #0f3460; + background:#16213e; + cursor:pointer; + display:flex; + align-items:center; + justify-content:center; +} +.compass-toggle:hover{border-color:#4fc3f7} +.compass-toggle-dot{ + width:10px;height:10px;border-radius:999px; + background:#6e7681; + box-shadow:0 0 0 2px rgba(0,0,0,.25) inset; +} +.compass--rotate-on .compass-toggle-dot{background:#d2ff1a} +.compass-dial{ + position:relative; + width:34px;height:34px; + border-radius:999px; + border:1px solid rgba(210,255,26,.25); + background:rgba(13,17,23,.55); + display:flex; + align-items:center; + justify-content:center; +} +.compass-arrow{ + width:28px;height:28px; + transform-origin:50% 50%; + will-change:transform; +} +.compass-n{ + position:absolute; + top:-7px; + left:50%; + transform:translateX(-50%); + font-size:10px; + font-weight:900; + color:#d2ff1a; + text-shadow:0 1px 0 rgba(0,0,0,.65); + user-select:none; + pointer-events:none; +} +.compass--no-heading{ + opacity:.55; + border-color:rgba(139,148,158,.35); +} +.vessel-item{padding:7px;margin:4px 0;border:1px solid #0f3460;border-radius:4px;cursor:pointer;transition:background .15s} +.vessel-item:hover{background:rgba(255,255,255,.05)} +.vessel-item.selected{background:rgba(210,255,26,.1);border-color:#d2ff1a} +.vessel-item.vessel-item--no-pos{opacity:.55;cursor:default} +.vessel-item.vessel-item--no-pos:hover{background:transparent} +.vessel-item.vessel-item--no-pos .mmsi{color:#8b949e} +.vessel-item .coords.coords--no-pos{color:#8b949e;font-style:italic} +.vessel-item .vessel-mmsi-row{display:flex;align-items:center;justify-content:space-between;gap:8px} +.vessel-item .mmsi{font-weight:700;color:#4fc3f7;flex:1;min-width:0;word-break:break-all} +.vessel-item .vessel-flag{font-size:18px;line-height:1;flex-shrink:0;opacity:.95} +.vessel-item .callsign-row{font-size:11px;color:#8b949e} +.vessel-item .name{color:#8899aa;font-size:11px} +.vessel-item .coords{color:#667;font-size:10px} +#range-filter{border-top:1px solid #0f3460;margin-top:8px;padding-top:6px} +#range-label{font-size:11px;color:#8899aa;margin-bottom:4px} +#range-slider{width:100%;height:4px;-webkit-appearance:none;appearance:none;background:#0f3460;border-radius:2px;outline:none;cursor:pointer} +#range-slider::-webkit-slider-thumb{-webkit-appearance:none;width:14px;height:14px;border-radius:50%;background:#d2ff1a;cursor:pointer;border:none} +#range-slider::-moz-range-thumb{width:14px;height:14px;border-radius:50%;background:#d2ff1a;cursor:pointer;border:none} + +/* Generic sliders (danger radii) */ +input[type="range"]#set-warn-radius-slider, +input[type="range"]#set-near-radius-slider{ + height:4px; + -webkit-appearance:none; + appearance:none; + background:#0f3460; + border-radius:2px; + outline:none; + cursor:pointer; +} +input[type="range"]#set-warn-radius-slider::-webkit-slider-thumb{ + -webkit-appearance:none; + width:14px;height:14px;border-radius:50%; + background:#f85149; + border:none; +} +input[type="range"]#set-warn-radius-slider::-moz-range-thumb{ + width:14px;height:14px;border-radius:50%; + background:#f85149;border:none; +} +input[type="range"]#set-near-radius-slider::-webkit-slider-thumb{ + -webkit-appearance:none; + width:14px;height:14px;border-radius:50%; + background:#f0883e; + border:none; +} +input[type="range"]#set-near-radius-slider::-moz-range-thumb{ + width:14px;height:14px;border-radius:50%; + background:#f0883e;border:none; +} +.vessel-item .dist{color:#d2ff1a;font-size:10px;font-weight:600;display:inline-flex;gap:6px;align-items:baseline} +.vessel-item .dist .dist-val{color:#d2ff1a;font-weight:700} +.vessel-item .dist .brg-val{color:#8b949e;font-weight:600} +.vessel-item .dist .brg-val::before{content:"\2192 ";opacity:.6} + +/* Unified SOG/COG/HDG stats row (desktop). Mobile variants override this via + .sidebar--compact and .targets-list selectors later in the file. */ +.vessel-item .compact-stats{ + display:flex;flex-wrap:wrap;gap:4px 8px; + margin-top:4px; + font-family:ui-monospace,Menlo,Consolas,monospace; + font-size:11px; +} +.vessel-item .compact-stats > span{ + display:inline-flex;gap:4px;align-items:baseline; + padding:1px 5px; + background:rgba(15,52,96,.4); + border:1px solid rgba(79,195,247,.16); + border-radius:3px; + cursor:help; + white-space:nowrap; +} +.vessel-item .compact-stats > span:hover{border-color:rgba(79,195,247,.45);background:rgba(15,52,96,.7)} +.vessel-item .compact-stats .k{color:#8b949e;font-weight:600;font-size:9px;letter-spacing:.4px;text-transform:uppercase} +.vessel-item .compact-stats b{color:#e0e0e0;font-weight:700} +#cursor-coords{position:absolute;z-index:1000;bottom:8px;left:8px;background:rgba(22,33,62,.9);padding:5px 10px;border-radius:4px;font-family:monospace;font-size:11px;color:#8899aa} + +/* Danger banner (ownship proximity) */ +.global-banners{ + position:fixed; + left:50%; + top:92px; /* stacked under conn-banner */ + transform:translateX(-50%); + z-index:2490; + display:flex; + flex-direction:column; + align-items:center; + gap:8px; + pointer-events:none; +} +.danger-banner{ + padding:9px 16px; + border-radius:999px; + background:rgba(248,81,73,.92); + border:2px solid rgba(0,0,0,.35); + color:#ffffff; + font-size:14px; + font-weight:900; + letter-spacing:.8px; + text-transform:uppercase; + box-shadow:0 10px 26px rgba(0,0,0,.55); + user-select:none; + pointer-events:none; +} + +/* Highlight nearby targets */ +.leaflet-marker-icon.vessel-nearby{ + filter: drop-shadow(0 0 5px rgba(210,255,26,.9)) drop-shadow(0 0 10px rgba(210,255,26,.35)); +} + +.vessel-icon{will-change:transform} +.vessel-icon--svg{line-height:0;display:flex;align-items:flex-end;justify-content:center} +.vessel-icon--svg svg{vertical-align:bottom} +.leaflet-marker-icon{transition:none!important} +.vessel-overlay-icon{z-index:1000!important;pointer-events:none} +.vessel-overlay-lost{ + background:transparent!important; + border:0!important; +} +.vessel-lost-dot{ + display:block; + width:16px; + height:16px; + border-radius:50%; + background:#8b949e; + border:1px solid #111; + box-shadow:0 1px 3px rgba(0,0,0,.75); +} + +/* AIS aids (base station / buoy) */ +.ais-base-station-icon{ + /* base_station.svg is black strokes only; make it visible on dark map */ + filter: invert(1) brightness(1.25) drop-shadow(0 0 2px rgba(0,0,0,.8)); +} +.ais-buoy-icon{ + filter: drop-shadow(0 0 2px rgba(0,0,0,.75)); +} +.ais-bs-divicon,.ais-aton-divicon{ + background:transparent!important; + border:0!important; +} +.ais-bs-symbol,.ais-aton-symbol{ + position:relative; + width:30px; + height:30px; + box-sizing:border-box; + display:flex; + align-items:center; + justify-content:center; + color:#f0f6fc; + text-shadow:0 1px 2px rgba(0,0,0,.9); + filter:drop-shadow(0 1px 3px rgba(0,0,0,.8)); +} +.ais-bs-symbol{ + border:2px solid #4fc3f7; + border-radius:50%; + background:#0d1117; +} +.ais-bs-mast{ + position:absolute; + left:14px; + top:7px; + width:2px; + height:17px; + background:#c9d1d9; + border-radius:2px; +} +.ais-bs-mast::before,.ais-bs-mast::after{ + content:""; + position:absolute; + left:-6px; + width:14px; + height:2px; + background:#c9d1d9; + border-radius:2px; +} +.ais-bs-mast::before{top:5px} +.ais-bs-mast::after{top:17px} +.ais-bs-waves::before,.ais-bs-waves::after{ + content:""; + position:absolute; + left:50%; + transform:translateX(-50%); + border:2px solid #4fc3f7; + border-bottom:0; + border-radius:18px 18px 0 0; +} +.ais-bs-waves::before{top:4px;width:16px;height:8px} +.ais-bs-waves::after{top:1px;width:24px;height:12px;opacity:.75} +.ais-aton-symbol{ + border:2px solid #0d1117; + border-radius:50%; + background:#667; +} +.ais-aton-symbol--fixed{border-radius:6px} +.ais-aton-symbol--floating{border-radius:50% 50% 48% 48%} +.ais-aton-label{ + z-index:1; + font-size:10px; + font-weight:900; + line-height:1; + letter-spacing:0; + color:#fff; +} +.ais-aton-symbol--cardinal-n{background:linear-gradient(180deg,#111 0 50%,#ffd84a 50% 100%)} +.ais-aton-symbol--cardinal-e{background:linear-gradient(180deg,#111 0 33%,#ffd84a 33% 66%,#111 66% 100%)} +.ais-aton-symbol--cardinal-s{background:linear-gradient(180deg,#ffd84a 0 50%,#111 50% 100%)} +.ais-aton-symbol--cardinal-w{background:linear-gradient(180deg,#ffd84a 0 33%,#111 33% 66%,#ffd84a 66% 100%)} +.ais-aton-symbol--lateral-port{background:#d72525} +.ais-aton-symbol--lateral-starboard{background:#168a45} +.ais-aton-symbol--preferred-port{background:linear-gradient(180deg,#d72525 0 35%,#168a45 35% 65%,#d72525 65% 100%)} +.ais-aton-symbol--preferred-starboard{background:linear-gradient(180deg,#168a45 0 35%,#d72525 35% 65%,#168a45 65% 100%)} +.ais-aton-symbol--isolated-danger{background:linear-gradient(180deg,#111 0 30%,#d72525 30% 70%,#111 70% 100%)} +.ais-aton-symbol--safe-water{background:linear-gradient(90deg,#d72525 0 32%,#fff 32% 68%,#d72525 68% 100%)} +.ais-aton-symbol--safe-water .ais-aton-label{color:#111;text-shadow:0 1px 2px rgba(255,255,255,.75)} +.ais-aton-symbol--special{background:#ffd84a} +.ais-aton-symbol--special .ais-aton-label{color:#111;text-shadow:none} +.ais-aton-symbol--wreck{background:linear-gradient(90deg,#1976d2 0 50%,#ffd84a 50% 100%)} +.ais-aton-symbol--light{background:#f7f7f7} +.ais-aton-symbol--light .ais-aton-label{color:#111;text-shadow:none} +.ais-aton-symbol--leading{background:#8b5cf6} +.ais-aton-symbol--racon{background:#c678dd} +.ais-aton-symbol--structure{background:#6e7681} +.ais-aton-symbol--light-vessel{background:#f0883e} +.ais-aton-symbol--reference,.ais-aton-symbol--generic{background:#30363d} +.ais-aid--virtual .ais-bs-symbol::after,.ais-aid--virtual .ais-aton-symbol::after, +.ais-aid--synthetic .ais-bs-symbol::after,.ais-aid--synthetic .ais-aton-symbol::after{ + content:""; + position:absolute; + inset:-6px; + border-radius:50%; + pointer-events:none; +} +.ais-aid--virtual .ais-bs-symbol::after,.ais-aid--virtual .ais-aton-symbol::after{ + border:2px dashed #4fc3f7; +} +.ais-aid--synthetic .ais-bs-symbol::after,.ais-aid--synthetic .ais-aton-symbol::after{ + border:2px dotted #d2ff1a; +} +.ais-aid--offposition .ais-aton-symbol{ + box-shadow:0 0 0 3px rgba(248,81,73,.45),0 0 12px rgba(248,81,73,.9); +} + +.transponder-hint{font-size:11px;color:#889;margin:0 0 12px;line-height:1.4} +.transponder-preview{margin:0;padding:10px;background:#0d1117;border:1px solid #30363d;border-radius:6px;font-size:10px;line-height:1.35;white-space:pre-wrap;word-break:break-all;max-height:55vh;overflow:auto;color:#c9d1d9} +.tp-dim{width:64px} +.transponder-raw-hex{width:100%;max-width:720px;box-sizing:border-box;font-family:ui-monospace,monospace;font-size:11px;padding:8px;background:#0d1117;border:1px solid #30363d;border-radius:6px;color:#c9d1d9;resize:vertical} + +.ship-type-select{max-width:100%;width:min(560px,100%)} +.ship-type-legend{font-size:10px;color:#889;margin:-4px 0 10px;line-height:1.4} +.ship-type-legend abbr{text-decoration:underline dotted;cursor:help} +.ship-editor-block{margin:14px 0;padding:12px;background:#0d1117;border:1px solid #30363d;border-radius:8px} +.ship-editor-wrap{display:flex;flex-wrap:wrap;gap:16px;align-items:flex-start} +.ship-editor-svg{flex-shrink:0;width:min(340px,100%);max-width:100%;height:auto;touch-action:none;cursor:default} +.ship-dim-main{stroke:#8b949e;stroke-width:1.25;fill:none} +.ship-dim-ext{stroke:#484f58;stroke-width:1;stroke-dasharray:3 4;stroke-linecap:square} +.ship-dim-txt{fill:#c9d1d9;font-size:10px;font-family:ui-sans-serif,system-ui,sans-serif} +.ship-dim-group{pointer-events:none} +.ship-axis-lbl--dyn{fill:#667;font-size:10px} +.ship-axis-lbl{fill:#667;font-size:11px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif} +.ship-hull{fill:rgba(79,195,247,.12);stroke:#4fc3f7;stroke-width:1.5} +.ship-grid{stroke:#30363d;stroke-width:0.6;stroke-dasharray:4 3} +.ship-gps-group{cursor:grab} +.ship-gps-group:active,body.ship-editor-dragging .ship-gps-group{cursor:grabbing} +.ship-gps-hit{fill:transparent;stroke:none;pointer-events:all} +.ship-gps-dot{fill:#58a6ff;stroke:#1f6feb;stroke-width:1.5;pointer-events:none} +.ship-gps-lbl{fill:#e0e0e0;font-size:7px;font-weight:700;pointer-events:none;user-select:none;font-family:system-ui,sans-serif} +.ship-gps-group--template .ship-gps-dot{fill:#8b949e;stroke:#484f58;stroke-dasharray:2 2} +.ship-editor-legend{margin:0;padding-left:18px;max-width:320px;font-size:11px;color:#889;line-height:1.45} +.ship-editor-legend li{margin:4px 0} +.ship-editor-legend strong{color:#c9d1d9} +.ship-edge-handles{pointer-events:all} +.ship-handle circle{fill:rgba(210,255,26,.4);stroke:#c5e636;stroke-width:1.5} +.ship-handle--stern circle{cursor:ns-resize} +.ship-handle--starboard circle{cursor:ew-resize} +.ship-handle:active circle{fill:rgba(210,255,26,.65)} + +#ownship-panel{position:absolute;z-index:1000;bottom:8px;right:8px;width:280px;background:rgba(22,33,62,.95);padding:10px 14px;border-radius:6px;font-size:12px;box-shadow:0 2px 8px rgba(0,0,0,.4)} +#ownship-panel h4{margin:0 0 5px;font-size:12px;border-bottom:1px solid #0f3460;padding-bottom:4px;color:#8899aa} +#ownship-panel .row{margin:2px 0} +#ownship-panel .label{color:#667} +.ownship-btn{display:inline-block;padding:4px 10px;margin:3px 3px 0 0;border:1px solid #0f3460;border-radius:4px;background:#16213e;cursor:pointer;font-size:11px;color:#8899aa;transition:all .15s} +.ownship-btn:hover{border-color:#4fc3f7;color:#ccc} +.ownship-btn.active{background:#d2ff1a;border-color:#a8cc14;color:#1a1a2e;font-weight:700} +.ownship-icon{will-change:transform} + +/* Mobile panel bar (hidden on desktop) */ +#mob-panel-bar{display:none} + +/* ===== Mobile map panels ===== + Also enable for phone landscape (small height). */ +@media(max-width:600px), (max-height:520px) and (pointer:coarse){ + :root{ + /* mob-panel-bar is disabled, but the vars are kept at 0 so positioning + calculations (sidebar/ownship-panel/HUD) still evaluate cleanly. */ + --mob-panel-bar-height:0px; + --mob-panel-bar-width:0px; + /* JS may set --vv-bottom for WebView/browser UI occlusions */ + --vv-bottom: 0px; + --vv-top: 0px; + --vv-right: 0px; + --vv-left: 0px; + --safe-bottom: env(safe-area-inset-bottom, 0px); + --safe-right: env(safe-area-inset-right, 0px); + } + @supports (padding: max(0px, 0px)){ + :root{ + --safe-bottom: max(env(safe-area-inset-bottom, 0px), var(--vv-bottom, 0px)); + --safe-right: max(env(safe-area-inset-right, 0px), var(--vv-right, 0px)); + } + } + #mob-panel-bar{ + display:flex; + position:fixed; + left:0;right:0; + bottom:0; + z-index:1100; + background:#16213e; + border-top:1px solid #0f3460; + padding-bottom:var(--safe-bottom); + touch-action:none; + overscroll-behavior:contain; + } + /* Visible drag-handle pill so users don't intuitively pull-to-refresh the page */ + #mob-panel-bar::before{ + content:""; + position:absolute; + left:50%;top:4px;transform:translateX(-50%); + width:44px;height:4px;border-radius:3px; + background:rgba(255,255,255,.28); + pointer-events:none; + } + #mob-panel-bar:active::before{background:rgba(210,255,26,.55)} + .mob-panel-tab{flex:1;padding:15px 0 9px;background:none;border:none;color:#8899aa;font-size:13px;font-weight:600;cursor:pointer;transition:color .15s,background .15s;border-top:2px solid transparent;touch-action:none} + .mob-panel-tab:active{background:rgba(255,255,255,.04)} + .mob-panel-tab.active{color:#d2ff1a;border-top-color:#d2ff1a} + #sidebar{position:absolute;top:auto;bottom:calc(var(--mob-panel-bar-height) + var(--safe-bottom));left:0;right:0;width:auto;max-height:60vh;border-radius:10px 10px 0 0;z-index:1100;display:none;overscroll-behavior:contain;padding-top:14px} + #sidebar.mob-open{display:flex} + #ownship-panel{position:absolute;top:auto;bottom:calc(var(--mob-panel-bar-height) + var(--safe-bottom));left:0;right:0;width:auto;max-height:60vh;overflow-y:auto;border-radius:10px 10px 0 0;z-index:1100;min-width:0;display:none;overscroll-behavior:contain;padding-top:14px} + #ownship-panel.mob-open{display:block} + + /* Drag-pill grip on top of the mobile bottom-sheet panels (sidebar & ownship-panel) + — gives the user an intuitive affordance to swipe them down to close, and avoids + accidentally triggering browser pull-to-refresh while interacting with the sheet. */ + #sidebar.mob-open::before, + #ownship-panel.mob-open::before{ + content:""; + position:absolute; + left:50%;top:5px;transform:translateX(-50%); + width:44px;height:4px;border-radius:3px; + background:rgba(255,255,255,.28); + pointer-events:none; + } + #sidebar.mob-swiping, + #ownship-panel.mob-swiping{ + transition:none; + will-change:transform; + } + #sidebar.mob-swiping::before, + #ownship-panel.mob-swiping::before{background:rgba(210,255,26,.55)} + #cursor-coords{bottom:calc(var(--mob-panel-bar-height) + var(--safe-bottom) + 8px);transition:bottom .2s} +} + +/* Landscape phone: move mobile buttons + panels to the right */ +@media (max-height:520px) and (pointer:coarse){ + #mob-panel-bar{ + left:auto; + right:0; + top:44px; /* below navbar */ + bottom:0; + width:calc(var(--mob-panel-bar-width) + var(--safe-right)); + flex-direction:column; + border-top:none; + border-left:1px solid #0f3460; + padding-bottom:0; + padding-right:var(--safe-right); + } + /* In landscape the bar is on the right — rotate the drag pill 90° */ + #mob-panel-bar::before{ + left:4px;top:50%;transform:translateY(-50%); + width:4px;height:44px;border-radius:3px; + } + .mob-panel-tab{ + padding:12px 10px 12px 14px; + border-top:none; + border-left:2px solid transparent; + } + .mob-panel-tab.active{ + border-top-color:transparent; + border-left-color:#d2ff1a; + } + + #sidebar, + #ownship-panel{ + top:0; + bottom:var(--safe-bottom); + left:auto; + right:calc(var(--mob-panel-bar-width) + var(--safe-right) + 8px); + width:min(420px, 40vw); + max-height:calc(100vh - 44px - var(--safe-bottom)); + border-radius:10px; + } + @supports (height: 100dvh){ + #sidebar, + #ownship-panel{ + max-height:calc(100dvh - 44px - var(--safe-bottom)); + } + } + + #cursor-coords{ + bottom:8px; + } +} + +/* ===== Stats page ===== */ +#page-stats{padding:20px;overflow-y:auto} +.stats-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:14px;margin-bottom:20px} +.stat-card{background:#16213e;border:1px solid #0f3460;border-radius:8px;padding:16px} +.stat-card .val{font-size:28px;font-weight:700;color:#d2ff1a} +.stat-card .lbl{font-size:12px;color:#8899aa;margin-top:2px} +.stat-card-test{border-color:#d29922}.stat-card-test .val{color:#f0883e} +.test-ch-detail{font-size:11px;color:#8899aa;margin-top:4px;display:flex;gap:10px}.test-ch-detail b{color:#e0e0e0} +.stat-section{margin-top:20px} +.stat-section h3{font-size:14px;color:#8899aa;margin-bottom:10px;border-bottom:1px solid #0f3460;padding-bottom:6px} +.stat-table{width:100%;border-collapse:collapse;font-size:13px} +.stat-table th,.stat-table td{padding:6px 10px;text-align:left;border-bottom:1px solid #0f3460} +.stat-table th{color:#667;font-weight:400} +.stat-table td{color:#e0e0e0} +.stat-details{background:#16213e;border:1px solid #0f3460;border-radius:8px;padding:10px 12px} +.stat-details>summary{cursor:pointer;user-select:none;color:#4fc3f7;font-size:13px;font-weight:700;outline:none} +.stat-details>summary::-webkit-details-marker{display:none} +.stat-details>summary::before{content:"▸";display:inline-block;margin-right:8px;color:#667;transition:transform .15s} +.stat-details[open]>summary::before{transform:rotate(90deg)} +.stat-adv-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:12px;margin-top:10px} +.stat-adv-card{background:#0d1117;border:1px solid #0f3460;border-radius:8px;padding:10px 12px} +.stat-adv-title{font-size:12px;color:#8899aa;margin-bottom:6px;font-weight:700} +.stat-table-compact th,.stat-table-compact td{padding:4px 8px;font-size:12px} +.stat-k{color:#667;font-family:monospace} +.stat-v{color:#e0e0e0;font-family:monospace;text-align:right} +.stat-v.err{color:#f85149;font-weight:700} + +/* ===== Settings page ===== */ +#page-settings{padding:20px;overflow-y:auto} +#page-transponder{padding:20px;overflow-y:auto;box-sizing:border-box} +.settings-card{background:#16213e;border:1px solid #0f3460;border-radius:8px;padding:16px;margin-bottom:14px} +.settings-card h3{font-size:14px;color:#8899aa;margin-bottom:10px} +.settings-row{display:flex;justify-content:space-between;align-items:center;padding:8px 0;border-bottom:1px solid rgba(15,52,96,.5)} +.settings-row:last-child{border-bottom:none} +.settings-label{color:#ccc;font-size:13px} +.settings-value{color:#4fc3f7;font-size:13px;font-family:monospace} + +/* Network settings */ +.net-mode-btns{display:flex;gap:8px;margin:10px 0} +.net-mode-btn{flex:1;padding:10px 0;text-align:center;border:2px solid #0f3460;border-radius:6px;background:#1a1a2e;cursor:pointer;font-size:13px;font-weight:600;color:#8899aa;transition:all .2s} +.net-mode-btn:hover{border-color:#4fc3f7;color:#ccc} +.net-mode-btn.active{border-color:#d2ff1a;background:rgba(210,255,26,.1);color:#d2ff1a} +.net-input{background:#1a1a2e;border:1px solid #0f3460;color:#e0e0e0;padding:6px 10px;border-radius:4px;font-size:13px;font-family:monospace;width:200px} +.net-input:focus{outline:none;border-color:#4fc3f7} +.net-btn{padding:8px 20px;border:none;border-radius:4px;font-size:13px;font-weight:600;cursor:pointer;transition:all .2s} +.net-btn-primary{background:#d2ff1a;color:#1a1a2e} +.net-btn-primary:hover{background:#b8e016} +.net-btn-danger{background:#f85149;color:#fff} +.net-btn-danger:hover{background:#d63a32} +.net-btn:disabled{opacity:.4;cursor:not-allowed} +.net-status{display:inline-block;padding:3px 10px;border-radius:12px;font-size:11px;font-weight:700} +.net-status.ap{background:rgba(79,195,247,.15);color:#4fc3f7} +.net-status.wifi{background:rgba(210,255,26,.15);color:#d2ff1a} +.net-status.unknown{background:rgba(136,153,170,.15);color:#8899aa} +.net-scan-list{max-height:180px;overflow-y:auto;margin:8px 0} +.net-scan-item{display:flex;justify-content:space-between;align-items:center;padding:6px 10px;border-radius:4px;cursor:pointer;font-size:12px;color:#ccc;transition:background .15s} +.net-scan-item:hover{background:rgba(255,255,255,.05)} +.net-scan-item.selected{background:rgba(210,255,26,.1);color:#d2ff1a} +.net-signal{color:#667;font-size:11px} +.net-msg{padding:8px 12px;border-radius:4px;font-size:12px;margin-top:8px;display:none} +.net-msg.ok{display:block;background:rgba(35,134,54,.15);color:#3fb950} +.net-msg.err{display:block;background:rgba(248,81,73,.15);color:#f85149} +.net-msg.info{display:block;background:rgba(79,195,247,.15);color:#4fc3f7} +.net-toggle-adv{color:#4fc3f7;font-size:12px;cursor:pointer;margin-top:6px;display:inline-block} +.net-toggle-adv:hover{text-decoration:underline} +.net-advanced{display:none;margin-top:10px} +.net-advanced.open{display:block} + +/* ===== Logs page ===== */ +#page-logs.active{display:flex;flex-direction:column} +.logs-toolbar{display:flex;gap:8px;padding:8px 12px;background:#16213e;border-bottom:1px solid #0f3460;align-items:center;flex-shrink:0} +.logs-toolbar label{font-size:12px;color:#8899aa} +.logs-toolbar select,.logs-toolbar input{background:#1a1a2e;border:1px solid #0f3460;color:#e0e0e0;padding:4px 8px;border-radius:4px;font-size:12px} +.log-btn{padding:4px 12px;border:1px solid #0f3460;border-radius:4px;background:#16213e;color:#8899aa;cursor:pointer;font-size:12px;transition:all .15s} +.log-btn:hover{border-color:#4fc3f7;color:#ccc} +.log-btn.active{background:#d2ff1a;color:#1a1a2e;border-color:#a8cc14;font-weight:600} +#log-output{flex:1;overflow-y:auto;overflow-x:auto;padding:8px 12px;font-family:"Cascadia Mono","Fira Code",monospace;font-size:12px;line-height:1.6;white-space:pre;background:#0d1117} +.log-line{color:#8b949e} +.log-line .ts{color:#484f58} +.log-line.ais{color:#4fc3f7} +.log-line.gps{color:#d2ff1a} +.log-line.unknown{color:#f85149} + +/* ===== Config page ===== */ +#page-config.active{display:flex;flex-direction:column;background:#0d1117} +.config-toolbar{display:flex;align-items:center;gap:10px;padding:8px 12px;background:#161b22;border-bottom:1px solid #0f3460;flex-shrink:0;font-size:13px;color:#8899aa} +.config-svc-label{font-size:12px;color:#667} +.config-svc-badge{display:inline-block;padding:2px 10px;border-radius:12px;font-size:11px;font-weight:700} +.config-svc-badge.active{background:rgba(63,185,80,.15);color:#3fb950} +.config-svc-badge.inactive{background:rgba(248,81,73,.15);color:#f85149} +.config-svc-badge.unknown{background:rgba(136,153,170,.15);color:#8899aa} +.config-body{flex:1;display:flex;flex-direction:column;padding:12px;min-height:0} +.config-tabs{display:flex;align-items:center;gap:8px;margin-bottom:8px;flex-wrap:wrap} +.config-tab{padding:6px 10px;border:1px solid #0f3460;border-radius:6px;background:#1a1a2e;color:#8899aa;font-size:12px;font-weight:700;cursor:pointer} +.config-tab.active{border-color:#d2ff1a;color:#d2ff1a;background:rgba(210,255,26,.08)} +.config-file-hint{font-size:11px;color:#667;font-family:monospace} +.config-editor{flex:1;min-height:200px;background:#0d1117;color:#e0e0e0;border:1px solid #30363d;border-radius:6px;padding:12px;font-family:"Cascadia Mono","Fira Code","JetBrains Mono",monospace;font-size:13px;line-height:1.6;resize:none;tab-size:4;white-space:pre;overflow:auto} +.config-editor:focus{outline:none;border-color:#58a6ff} +.config-actions{display:flex;align-items:center;gap:8px;margin-top:10px;flex-wrap:wrap} +.config-msg{font-size:12px;white-space:nowrap} +.config-msg.ok{color:#3fb950} +.config-msg.err{color:#f85149} +.config-msg.info{color:#4fc3f7} +.config-hint{margin-top:8px;font-size:11px;color:#484f58;font-family:monospace} + +/* ===== Console (xterm.js) ===== */ +#page-console.active{display:flex;flex-direction:column;background:#0d1117} +.console-toolbar{display:flex;align-items:center;gap:10px;padding:8px 12px;background:#161b22;border-bottom:1px solid #0f3460;flex-shrink:0;font-size:12px;color:#8899aa} +.console-toolbar code{color:#4fc3f7;font-size:11px} +#terminal-wrap{flex:1;min-height:0;padding:0 8px 8px;box-sizing:border-box;display:none} +#terminal-wrap .xterm{height:100%;padding:6px 0} +#terminal-unavailable{display:none;padding:24px;color:#8899aa;font-size:13px;line-height:1.5;max-width:520px} + +/* ===== Slots TDMA section ===== */ +.slots-section{margin-top:20px} +.slots-header{display:flex;align-items:center;gap:8px;padding:12px 16px;background:#16213e;border:1px solid #0f3460;border-radius:8px;cursor:pointer;user-select:none;font-size:14px;color:#8899aa;transition:background .15s} +.slots-header:hover{background:#1a2744} +.slots-arrow{font-size:10px;color:#667;display:inline-block;transition:transform .2s} +.slots-arrow.open{transform:rotate(90deg)} +.slots-content{display:none;margin-top:8px} +.slots-content.open{display:block} +.slots-channels{display:grid;grid-template-columns:1fr 1fr;gap:14px} +@media(max-width:960px){.slots-channels{grid-template-columns:1fr}} +.slots-channel{background:#16213e;border:1px solid #0f3460;border-radius:8px;padding:14px} +.slots-ch-title{font-size:13px;font-weight:700;color:#4fc3f7;margin-bottom:6px} +.slots-info{font-size:12px;color:#8899aa;margin-bottom:8px;font-family:monospace} +.slots-no-data{font-size:12px;color:#484f58;font-style:italic} +.slots-channel canvas{display:block;border-radius:4px;background:#0d1117;max-width:100%} +.slots-canvas-wrap{display:block;overflow:auto;border-radius:4px;background:#0d1117} +.slots-canvas-wrap canvas{max-width:none} +.slots-legend{margin-top:12px;display:flex;gap:18px;font-size:12px;color:#8899aa} +.slots-legend-item{display:flex;align-items:center;gap:5px} +.slots-legend-box{display:inline-block;width:12px;height:12px;border-radius:2px} +.slots-legend-box.free{background:#238636} +.slots-legend-box.occ{background:#f85149} +.slots-test-send{margin-top:14px;display:flex;align-items:center;gap:8px;flex-wrap:wrap} +.slots-test-label{font-size:12px;color:#8899aa;white-space:nowrap} +.slots-test-input{background:#0d1117;border:1px solid #30363d;border-radius:4px;color:#e0e0e0;padding:5px 8px;font-size:12px;font-family:inherit} +.slots-test-input:focus{outline:none;border-color:#58a6ff} +.slots-test-btn{background:#238636;color:#fff;border:none;border-radius:4px;padding:5px 14px;font-size:12px;cursor:pointer;white-space:nowrap;transition:background .15s} +.slots-test-btn:hover{background:#2ea043} +.slots-test-btn:active{background:#196c2e} +.slots-test-btn:disabled{background:#21262d;color:#484f58;cursor:not-allowed} +.slots-test-status{font-size:11px;white-space:nowrap} +.slots-test-status.ok{color:#3fb950} +.slots-test-status.err{color:#f85149} +.slots-test-status.wait{color:#d29922} +.slots-selected-info{margin-top:8px;font-size:12px;color:#8899aa;font-family:monospace;min-height:16px} +.slots-bar{height:6px;border-radius:3px;background:#0d1117;margin-bottom:10px;overflow:hidden} +.slots-bar-fill{height:100%;border-radius:3px;transition:width .3s} +.slots-tooltip{position:fixed;z-index:9999;pointer-events:none;background:rgba(22,33,62,.95);border:1px solid #0f3460;border-radius:4px;padding:4px 8px;font-size:11px;color:#e0e0e0;font-family:monospace;white-space:nowrap;display:none;box-shadow:0 2px 8px rgba(0,0,0,.5)} +.slots-rssi-label{font-size:11px;color:#667;margin-bottom:4px} +.slots-channel canvas.rssi-chart{width:100%;height:90px;margin-bottom:8px} +.slots-rssi-legend{display:flex;gap:14px;font-size:11px;color:#8899aa;margin-top:4px;margin-bottom:6px} +.slots-rssi-legend span::before{content:'';display:inline-block;width:16px;height:2px;margin-right:4px;vertical-align:middle} + +/* =================================================================== + Vessel InfoWindow (MarineTraffic-style) + - Desktop: floating, draggable card anchored inside map area + - Mobile : full-width bottom-sheet above the mob-panel-bar + =================================================================== */ +.vinf{ + position:absolute; + z-index:1200; + top:64px; left:8px; + width:360px; + max-width:calc(100vw - 16px); + background:#16213e; + border:1px solid #0f3460; + border-radius:10px; + box-shadow:0 10px 28px rgba(0,0,0,.55); + color:#e0e0e0; + font-size:12px; + display:flex; + flex-direction:column; + overflow:hidden; + user-select:none; + touch-action:none; +} +.vinf[hidden]{display:none!important} +/* Drag/grip strip at the top (mobile pill + desktop subtle handle) */ +.vinf-grip{ + position:relative; + height:14px; + flex-shrink:0; + background:#0f3460; + cursor:grab; + display:none; +} +.vinf-grip::before{ + content:""; + position:absolute;left:50%;top:50%; + transform:translate(-50%,-50%); + width:40px;height:4px;border-radius:3px; + background:rgba(255,255,255,.22); +} +.vinf-grip:hover::before{background:rgba(255,255,255,.36)} +.vinf-grip:active{cursor:grabbing} +.vinf-header{ + display:flex; + align-items:center; + gap:8px; + padding:8px 10px; + background:#0f3460; + border-bottom:1px solid #0f3460; + cursor:grab; +} +.vinf-header:active{cursor:grabbing} +/* Collapsed-mode mini stats (hidden in the expanded view) */ +.vinf-mini{ + display:none; + gap:10px; + font-size:11px;color:#8b949e; + font-family:ui-monospace,monospace; +} +.vinf-mini b{color:#c9d1d9;font-weight:700} +/* Collapsed state: hide body/footer via clip; keep header + mini stats visible */ +.vinf-body,.vinf-footer{ + transition:max-height .22s ease, opacity .18s ease, padding .22s ease, border-color .18s ease; + will-change:max-height,opacity; + overflow:hidden; +} +.vinf--collapsed .vinf-sub{display:none} +.vinf--collapsed .vinf-mini{display:flex} +.vinf--collapsed .vinf-header{border-bottom-color:transparent} +.vinf--collapsed .vinf-body{max-height:0!important;opacity:0;padding-top:0;padding-bottom:0;pointer-events:none;border-color:transparent} +.vinf--collapsed .vinf-footer{max-height:0!important;opacity:0;padding-top:0;padding-bottom:0;border-top-color:transparent;pointer-events:none} +/* Collapse button icon swap (chevron down when expanded → becomes up when collapsed) */ +.vinf-ic-expand{display:block} +.vinf-ic-collapse{display:none} +.vinf--collapsed .vinf-ic-expand{display:none} +.vinf--collapsed .vinf-ic-collapse{display:block} +.vinf-icon{ + width:34px; height:34px; + flex-shrink:0; + display:flex; + align-items:center; + justify-content:center; + background:#0d1117; + border-radius:6px; + border:1px solid rgba(255,255,255,.08); +} +.vinf-icon svg{display:block;width:26px;height:26px} +.vinf-title{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px;line-height:1.2} +.vinf-name{ + font-size:14px;font-weight:700;color:#e0e0e0; + white-space:nowrap;overflow:hidden;text-overflow:ellipsis; +} +.vinf-sub{ + font-size:10px;color:#8b949e; + white-space:nowrap;overflow:hidden;text-overflow:ellipsis; + display:flex;align-items:center;gap:6px; +} +.vinf-flag{ + display:inline-block; + font-size:14px;line-height:1; +} +.vinf-btn{ + background:transparent;border:none;color:#8b949e; + cursor:pointer;padding:4px 6px;border-radius:4px; + display:flex;align-items:center;justify-content:center; +} +.vinf-btn:hover{color:#e0e0e0;background:rgba(255,255,255,.06)} +.vinf-btn svg{display:block;width:18px;height:18px} +.vinf-drag-handle{ + color:#667;padding:4px 2px;cursor:grab; + display:inline-flex;align-items:center; +} + +/* Body */ +.vinf-body{ + padding:10px; + display:flex; + flex-direction:column; + gap:10px; + max-height:60vh; + overflow-y:auto; + user-select:text; + touch-action:auto; +} +.vinf-voyage{ + display:grid; + grid-template-columns:1fr auto 1fr; + gap:6px 10px; + align-items:center; + background:#0d1117; + border:1px solid rgba(255,255,255,.05); + border-radius:6px; + padding:8px 10px; +} +.vinf-port{ + font-size:13px;font-weight:700;color:#c9d1d9; + min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap; +} +.vinf-port--right{text-align:right} +.vinf-port-country{color:#667;font-weight:500;margin-right:4px;font-size:11px} +.vinf-voyage-arrow{ + color:#4fc3f7;font-size:14px;display:flex;align-items:center;justify-content:center; + padding:0 2px; +} +.vinf-eta{ + grid-column:1/-1; + display:flex;justify-content:space-between;gap:10px; + font-size:11px;color:#8b949e; + border-top:1px dashed rgba(255,255,255,.08); + padding-top:6px;margin-top:2px; +} +.vinf-eta b{color:#c9d1d9} +.vinf-progress{ + height:4px;background:#0d1117;border-radius:2px;overflow:hidden; + margin:6px 0 2px; + grid-column:1/-1; +} +.vinf-progress-fill{height:100%;background:linear-gradient(90deg,#4fc3f7,#d2ff1a);border-radius:2px} + +/* 2x2 grid: nav / speed-course / draught / heading */ +.vinf-grid{ + display:grid; + grid-template-columns:1fr 1fr; + gap:1px; + background:rgba(255,255,255,.05); + border-radius:6px; + overflow:hidden; +} +.vinf-cell{ + background:#0d1117; + padding:7px 10px; + display:flex;flex-direction:column;gap:3px; + min-width:0; +} +.vinf-cell-lbl{font-size:10px;color:#667;text-transform:uppercase;letter-spacing:.4px} +.vinf-cell-val{font-size:13px;font-weight:700;color:#c9d1d9;white-space:nowrap;overflow:hidden;text-overflow:ellipsis} +.vinf-cell-val--wrap{white-space:normal;line-height:1.25;overflow:visible;text-overflow:clip} +.vinf-cell-val.dim{color:#667;font-weight:500;font-style:italic} + +/* Identifiers (MMSI/IMO/Callsign) */ +.vinf-ids{ + display:flex;flex-wrap:wrap;gap:6px 14px; + font-size:11px;color:#8b949e; + padding:4px 2px; +} +.vinf-ids b{color:#c9d1d9;font-family:ui-monospace,monospace} +.vinf-ids a{color:#4fc3f7;text-decoration:none} +.vinf-ids a:hover{text-decoration:underline} + +/* Signal block */ +.vinf-signal{ + display:flex;align-items:center;gap:10px; + padding:6px 10px; + background:#0d1117; + border-radius:6px; + font-size:11px;color:#8b949e; +} +.vinf-signal-bars{ + display:inline-flex;align-items:flex-end;gap:2px; + height:14px; +} +.vinf-signal-bar{ + width:3px;background:#30363d;border-radius:1px; +} +.vinf-signal-bar.on{background:#3fb950} +.vinf-signal-bar.warn{background:#d29922} +.vinf-signal-bar.bad{background:#f85149} +.vinf-signal b{color:#c9d1d9} +.vinf-signal .sig-db{font-family:ui-monospace,monospace;color:#c9d1d9} + +/* Actions */ +.vinf-actions{ + display:flex;flex-wrap:wrap;gap:6px; + padding:2px 0; +} +.vinf-act{ + display:inline-flex;align-items:center;gap:5px; + padding:6px 10px; + background:#16213e; + border:1px solid #0f3460; + border-radius:6px; + color:#c9d1d9; + font-size:11px;font-weight:600; + cursor:pointer; + transition:border-color .15s,color .15s,background .15s; +} +.vinf-act:hover{border-color:#4fc3f7;color:#e0e0e0} +.vinf-act.primary{background:rgba(79,195,247,.12);border-color:#4fc3f7;color:#4fc3f7} +.vinf-act.primary:hover{background:rgba(79,195,247,.22)} +.vinf-act svg{width:14px;height:14px} + +/* Footer */ +.vinf-footer{ + padding:6px 10px; + background:#0d1117; + font-size:10px;color:#667; + display:flex;justify-content:space-between;gap:8px;flex-wrap:wrap; + border-top:1px solid rgba(255,255,255,.04); +} +.vinf-footer b{color:#c9d1d9} + +/* Mobile: bottom-sheet — fixed above the mob-panel-bar */ +@media(max-width:600px), (max-height:520px) and (pointer:coarse){ + .vinf{ + position:absolute; + top:auto; + left:0;right:0; + bottom:calc(var(--mob-panel-bar-height, 42px) + var(--safe-bottom, 0px)); + width:auto; + max-width:none; + border-radius:10px 10px 0 0; + max-height:70vh; + transition:max-height .22s ease; + } + .vinf-grip{display:block} + .vinf-header{cursor:default} + .vinf-body{max-height:50vh} + /* Collapsed on mobile: shrink the whole sheet to just the grip + header */ + .vinf.vinf--collapsed{max-height:72px} +} +@media (max-height:520px) and (pointer:coarse){ + /* landscape: panel on the right side like sidebar */ + .vinf{ + top:52px; + bottom:var(--safe-bottom,0px); + left:auto;right:calc(var(--mob-panel-bar-width,72px) + var(--safe-right,0px) + 8px); + width:min(420px,44vw); + max-width:none; + border-radius:10px; + } + /* Landscape collapsed: shrink width instead of height for a cleaner look */ + .vinf.vinf--collapsed{ + width:min(260px,38vw); + max-height:none; + } +} + +/* =================================================================== + Map controls (centre / north-up / ruler / one-hand / zoom) + =================================================================== */ +.map-controls{ + position:absolute; + z-index:1000; + top:8px; left:50%; + transform:translateX(-50%); + display:flex;gap:4px; + background:rgba(22,33,62,.92); + border:1px solid #0f3460; + border-radius:8px; + padding:3px; + box-shadow:0 2px 8px rgba(0,0,0,.4); +} +.map-ctrl{ + width:32px;height:32px; + border:none;border-radius:6px; + background:transparent;color:#c9d1d9; + cursor:pointer; + display:flex;align-items:center;justify-content:center; + font-size:16px;font-weight:700;line-height:1; + transition:background .15s,color .15s; +} +.map-ctrl:hover{background:rgba(255,255,255,.08);color:#d2ff1a} +.map-ctrl:active{background:rgba(255,255,255,.14)} +.map-ctrl.active{background:rgba(210,255,26,.14);color:#d2ff1a} +.map-ctrl svg{display:block} + +/* On mobile, put the control strip to the top-right corner to leave more + horizontal space for the conn-banner, and make it vertical. */ +@media(max-width:600px){ + .map-controls{ + top:52px; + left:auto;right:8px; + transform:none; + flex-direction:column; + } +} + +/* Leaflet scale bar (enabled via L.control.scale) */ +.leaflet-control-scale{ + margin:0 !important; +} +.leaflet-control-scale-line{ + background:rgba(22,33,62,.85)!important; + color:#d2ff1a!important; + border:1px solid rgba(210,255,26,.35)!important; + border-top:none!important; + padding:1px 6px!important; + font-size:10px!important; + line-height:1.3!important; + font-weight:600; +} + +/* =================================================================== + One-hand zoom pad (big thumb-reach buttons, toggled on) + =================================================================== */ +.onehand-pad{ + position:absolute; + right:8px; bottom:60px; + z-index:1150; + display:flex;flex-direction:column;gap:8px; +} +.onehand-pad[hidden]{display:none!important} +.onehand-btn{ + width:54px;height:54px; + border-radius:50%; + border:1px solid #0f3460; + background:rgba(22,33,62,.92); + color:#d2ff1a; + font-size:26px;font-weight:800; + cursor:pointer; + display:flex;align-items:center;justify-content:center; + box-shadow:0 4px 12px rgba(0,0,0,.45); + touch-action:manipulation; +} +.onehand-btn:active{background:rgba(22,33,62,1);transform:scale(.96)} +.onehand-btn#oh-center{color:#4fc3f7} +/* On landscape-compact, push it next to the mob-panel-bar on the right side */ +@media (max-height:520px) and (pointer:coarse){ + .onehand-pad{right:calc(var(--mob-panel-bar-width,72px) + var(--safe-right,0px) + 8px); bottom:16px} +} + +/* =================================================================== + Ruler tool + =================================================================== */ +.ruler-hud{ + position:absolute;z-index:1250; + top:58px; left:50%;transform:translateX(-50%); + background:rgba(22,33,62,.94); + border:1px solid #4fc3f7; + border-radius:999px; + padding:6px 14px; + color:#c9d1d9; + font-size:12px;font-weight:600; + box-shadow:0 4px 14px rgba(0,0,0,.5); + pointer-events:auto; + display:flex;align-items:center;gap:10px; +} +.ruler-hud[hidden]{display:none!important} +.ruler-hud .dist{color:#d2ff1a;font-weight:700;font-family:ui-monospace,monospace} +.ruler-hud .hint{color:#8b949e;font-weight:500} +.ruler-hud button{ + background:transparent;border:1px solid transparent;color:#8b949e; + font-size:12px;cursor:pointer;border-radius:4px;padding:2px 6px; +} +.ruler-hud button:hover{color:#f85149;border-color:#f85149} +body.ruler-active .leaflet-container{cursor:crosshair !important} + +/* =================================================================== + Targets tab page (full list; desktop has same data in #sidebar) + =================================================================== */ +#page-targets.active{display:flex;flex-direction:column;background:#1a1a2e} +.targets-page-inner{ + flex:1;min-height:0; + display:flex;flex-direction:column; + padding:14px 16px 0; + max-width:860px; + margin:0 auto; + width:100%; + box-sizing:border-box; +} +.targets-page-inner h3{ + font-size:17px;margin:0 0 10px;color:#c9d1d9; + border-bottom:1px solid #0f3460;padding-bottom:8px; +} +.targets-list{ + flex:1;min-height:0;overflow-y:auto;padding-bottom:16px; + display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr)); + gap:8px;align-content:start; +} +.targets-list .vessel-item{margin:0;background:#16213e} +.targets-list .vessel-item:hover{background:rgba(255,255,255,.05)} + +/* Only show the page-targets tab on mobile; on desktop we keep the sidebar */ +.nav-tab--targets{display:none} +@media(max-width:600px){ + .nav-tab--targets{display:inline-block} +} + +/* =================================================================== + Mobile compact "nearby" sidebar: MMSI / COG / SOG / HDG / BRG / DIST + =================================================================== */ +.sidebar--compact .vessel-item{ + display:grid; + grid-template-columns:auto 1fr auto; + grid-template-areas: + "mmsi name dist" + "stats stats stats"; + gap:2px 8px; + padding:6px 8px; + align-items:center; +} +.sidebar--compact .vessel-item .vessel-mmsi-row{grid-area:mmsi;display:flex;gap:6px;align-items:center} +.sidebar--compact .vessel-item .mmsi{font-size:12px} +.sidebar--compact .vessel-item .name{grid-area:name;font-size:11px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis} +.sidebar--compact .vessel-item .dist{grid-area:dist;display:flex;flex-direction:column;align-items:flex-end;font-size:11px;line-height:1.15;gap:1px} +.sidebar--compact .vessel-item .dist .dist-val{color:#d2ff1a;font-weight:700} +.sidebar--compact .vessel-item .dist .brg-val{color:#8b949e;font-size:10px} +.sidebar--compact .vessel-item .dist .brg-val::before{content:"\2192 ";opacity:.6} +.sidebar--compact .vessel-item .compact-stats{ + grid-area:stats; + display:grid; + grid-template-columns:repeat(auto-fit, minmax(58px, 1fr)); + gap:3px 6px; + font-size:10px;color:#8b949e;font-family:ui-monospace,monospace; +} +.sidebar--compact .vessel-item .compact-stats > span{ + display:flex;align-items:baseline;gap:4px; + white-space:nowrap;overflow:hidden;text-overflow:ellipsis; + cursor:help; +} +.sidebar--compact .vessel-item .compact-stats .k{color:#667;font-weight:500} +.sidebar--compact .vessel-item .compact-stats b{color:#c9d1d9;font-weight:600} +.sidebar--compact .vessel-item .callsign-row, +.sidebar--compact .vessel-item .coords, +.sidebar--compact .vessel-item > div:not(.vessel-mmsi-row):not(.name):not(.dist):not(.compact-stats){ + display:none; +} + +/* =================================================================== + Targets tab full list: readable stats grid with labels + =================================================================== */ +.targets-list .vessel-item{ + display:grid; + grid-template-columns:auto 1fr auto; + grid-template-areas: + "mmsi name dist" + "stats stats stats"; + gap:4px 10px; + padding:10px 12px; +} +.targets-list .vessel-item .vessel-mmsi-row{grid-area:mmsi;display:flex;gap:6px;align-items:center} +.targets-list .vessel-item .name{grid-area:name;font-size:13px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis} +.targets-list .vessel-item .dist{grid-area:dist;display:flex;flex-direction:column;align-items:flex-end;gap:2px;font-size:12px;line-height:1.15} +.targets-list .vessel-item .dist .dist-val{color:#d2ff1a;font-weight:700} +.targets-list .vessel-item .dist .brg-val{color:#8b949e;font-size:11px;font-weight:600} +.targets-list .vessel-item .dist .brg-val::before{content:"\2192 ";opacity:.6} +.targets-list .vessel-item > div:not(.vessel-mmsi-row):not(.name):not(.dist):not(.compact-stats){ + display:none; +} +.targets-list .compact-stats{ + grid-area:stats; + display:grid; + grid-template-columns:repeat(auto-fill, minmax(84px, 1fr)); + gap:4px 6px; + margin-top:2px; + font-family:ui-monospace,monospace; +} +.targets-list .compact-stats > span{ + display:flex;flex-direction:column; + align-items:flex-start; + padding:4px 6px; + background:rgba(15,52,96,.55); + border:1px solid rgba(79,195,247,.18); + border-radius:5px; + cursor:help; + line-height:1.1; + min-width:0; +} +.targets-list .compact-stats > span:hover{ + border-color:rgba(79,195,247,.5); + background:rgba(15,52,96,.85); +} +.targets-list .compact-stats .k{ + font-size:9px;font-weight:700; + letter-spacing:.6px; + color:#8b949e; + text-transform:uppercase; +} +.targets-list .compact-stats b{ + font-size:12px;color:#e0e0e0;font-weight:700; + max-width:100%; + white-space:nowrap;overflow:hidden;text-overflow:ellipsis; +} + +/* =================================================================== + Nearby HUD — semi-transparent corner overlay that shows own ship + (speed / coords / compass) plus up to 5 nearest targets (or a single + currently selected target with a clear-X). Replaces the mobile + bottom tab-bar as the primary in-field readout. + =================================================================== */ +.nearby-hud{ + position:absolute; + z-index:1000; + left:8px; + bottom:8px; + width:min(290px, 42vw); + max-height:min(50vh, 540px); + background:rgba(13,17,23,.72); + backdrop-filter:blur(6px); + -webkit-backdrop-filter:blur(6px); + border:1px solid rgba(210,255,26,.22); + border-radius:10px; + color:#c9d1d9; + font-family:ui-monospace,Menlo,Consolas,monospace; + font-size:11px; + box-shadow:0 4px 14px rgba(0,0,0,.45); + display:flex;flex-direction:column; + overflow:hidden; + transition:max-height .2s ease, opacity .2s ease; +} +.nearby-hud.is-empty{opacity:.6} +.nearby-hud.is-collapsed{ + max-height:54px; +} +.nearby-hud.is-collapsed .nearby-hud__list, +.nearby-hud.is-collapsed .nhud-own__coords, +.nearby-hud.is-collapsed .nhud-own__src{display:none} +.nearby-hud.is-collapsed .nhud-compass{transform:scale(.82);margin-right:4px} + +.nearby-hud__own{ + padding:6px 8px 5px 8px; + border-bottom:1px solid rgba(79,195,247,.14); +} +.nhud-own__row{display:flex;gap:8px;align-items:center} +.nhud-own__info{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px} +.nhud-own__line{display:flex;gap:6px;align-items:baseline} +.nhud-k{color:#8b949e;font-weight:600;font-size:10px;text-transform:uppercase;letter-spacing:.4px} +.nhud-own__line b{color:#d2ff1a;font-weight:700;font-size:13px} +.nhud-own__coords{color:#c9d1d9;font-size:10px;opacity:.85;white-space:nowrap;overflow:hidden;text-overflow:ellipsis} +.nhud-own__src{color:#667;font-size:9px;text-transform:uppercase;letter-spacing:.5px} +.nhud-own__src.nhud-own__src--err{color:#f85149;text-transform:none;letter-spacing:0} + +.nhud-compass{ + position:relative; + display:inline-flex;align-items:center;justify-content:center; + flex:0 0 auto; +} +.nhud-compass svg{display:block} +.nhud-compass-needle{ + transition:transform .25s ease; +} +.nhud-compass.no-data .nhud-compass-needle{opacity:.3} +.nhud-compass-val{ + position:absolute;left:50%;top:55%;transform:translate(-50%, -50%); + pointer-events:none; + font-size:9px;font-weight:700;color:#d2ff1a; + background:rgba(13,17,23,.6);padding:0 3px;border-radius:3px; + letter-spacing:.3px; + display:none; +} +.nhud-compass.has-val .nhud-compass-val{display:inline-block} +.nhud-compass.from-phone{box-shadow:0 0 0 2px rgba(79,195,247,.28) inset;border-radius:50%} +.nhud-compass.is-rotating-map{box-shadow:0 0 0 2px rgba(210,255,26,.55) inset;border-radius:50%} +.nhud-compass:hover{filter:brightness(1.1)} + +.nearby-hud__list{ + flex:1;min-height:0; + overflow-y:auto; + overscroll-behavior:contain; +} +.nearby-hud__list::-webkit-scrollbar{width:4px} +.nearby-hud__list::-webkit-scrollbar-thumb{background:rgba(210,255,26,.25);border-radius:2px} + +.nhud-item{ + position:relative; + padding:5px 8px 5px 8px; + border-bottom:1px solid rgba(79,195,247,.08); + cursor:pointer; + transition:background .12s; +} +.nhud-item:last-child{border-bottom:none} +.nhud-item:hover{background:rgba(210,255,26,.07)} +.nhud-item.is-selected{background:rgba(210,255,26,.12);border-left:3px solid #d2ff1a;padding-left:5px} +.nhud-item__top{display:flex;gap:6px;align-items:baseline;justify-content:space-between} +.nhud-item__id{ + display:flex;gap:4px;align-items:baseline;min-width:0;flex:1; + white-space:nowrap;overflow:hidden;text-overflow:ellipsis; +} +.nhud-item__name{color:#c9d1d9;font-weight:600;font-size:11px;overflow:hidden;text-overflow:ellipsis} +.nhud-item__callsign{color:#8b949e;font-size:9px} +.nhud-item__mmsi{color:#667;font-size:9px;font-weight:500} +.nhud-item__dist{color:#d2ff1a;font-weight:700;font-size:11px;white-space:nowrap;flex:0 0 auto} +.nhud-item__bot{display:flex;gap:8px;align-items:baseline;margin-top:1px;color:#8b949e;font-size:10px} +.nhud-item__bot .k{color:#667;margin-right:2px;font-size:9px} +.nhud-item__bot b{color:#c9d1d9;font-weight:600} +.nhud-item__brg::before{content:"\2192 ";opacity:.6} +.nhud-item__coords{color:#667;font-size:9px;margin-top:1px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis} + +.nhud-item__clear{ + position:absolute;right:4px;top:4px; + width:20px;height:20px;border-radius:50%; + background:rgba(248,81,73,.15);color:#f85149; + border:1px solid rgba(248,81,73,.35); + display:flex;align-items:center;justify-content:center; + cursor:pointer;font-size:12px;font-weight:700;line-height:1; + padding:0; +} +.nhud-item__clear:hover{background:rgba(248,81,73,.3)} + +.nearby-hud__toggle{ + position:absolute;top:2px;right:2px; + width:22px;height:22px; + background:transparent;border:none;border-radius:4px; + color:#8b949e;cursor:pointer; + display:flex;align-items:center;justify-content:center; + z-index:2; + transition:transform .2s ease, color .15s; +} +.nearby-hud__toggle:hover{color:#d2ff1a} +.nearby-hud.is-collapsed .nearby-hud__toggle{transform:rotate(180deg)} + +.nhud-empty{ + padding:10px 8px;color:#667;text-align:center;font-size:10px; +} + +/* Mobile layout: wider, anchored above the mob-panel-bar. */ +@media(max-width:600px), (max-height:520px) and (pointer:coarse){ + .nearby-hud{ + left:8px;right:8px; + width:auto; + bottom:calc(var(--mob-panel-bar-height, 42px) + var(--safe-bottom, 0px) + 8px); + max-height:38vh; + } + /* When the bottom sheets (sidebar / ownship-panel) are open, hide the HUD to avoid stacking. */ + #sidebar.mob-open ~ .nearby-hud, + #ownship-panel.mob-open ~ .nearby-hud{display:none} +} +@media (max-height:520px) and (pointer:coarse){ + .nearby-hud{ + left:8px; + bottom:calc(8px + var(--safe-bottom, 0px)); + right:calc(var(--mob-panel-bar-width, 72px) + var(--safe-right, 0px) + 8px); + max-height:calc(100vh - 52px - var(--safe-bottom, 0px)); + } +} diff --git a/static/js/app.js b/static/js/app.js new file mode 100644 index 0000000..ee1fdc2 --- /dev/null +++ b/static/js/app.js @@ -0,0 +1,6077 @@ +// ===================== Tab switching ===================== +const APP_BUILD = '2026-04-30h'; +try { console.log('[AISMap] app.js build', APP_BUILD, 'loaded at', new Date().toISOString()); } catch (e) {} +let currentTab = 'map'; + +// ===================== ais_hub state (fed by WebSocket /ws) ===================== +// Всё AIS/GPS состояние хранится здесь и наполняется live-событиями ais_hub. +// Рендер-функции (updateVessels/…/updateSlots) читают отсюда вместо /api/*. +const AisHub = { + vessels: new Map(), // mmsi -> vessel (наш внутренний shape) + baseStations: new Map(), // mmsi -> base station + atons: new Map(), // mmsi -> buoy/AtoN + ownship: null, // последний GPS-fix из ais_hub (наш shape) + stats: null, // сырой /api/v1/stats + sysinfo: null, // локальный /api/sysinfo + slots: {A: null, B: null}, // per-channel slot bitmap/occupancy + detail + slotDetail: {A: null, B: null}, + rssiHistory: {A: [], B: []}, // история noise_floor/threshold per minute + livePower: {A: [], B: []}, // мгновенная мощность ~10 Гц + signalEvents: {A: [], B: []}, // события декодов (sparkline сигнала) + snapshotLoaded: false, + wsOpen: false, + lastEventTs: 0, +}; +window.AisHub = AisHub; + +const AIS_LIVE_POWER_MAX = 600; +const AIS_SIGNAL_EVENTS_MAX = 300; +const AIS_RSSI_HISTORY_MAX = 120; + +// ITU-R M.1371-5 / Message 21 Table 74: тип средства навигационного оборудования (СНО / AtoN). +const AIS_ATON_TYPE_LABELS = { + 0: 'Тип СНО не указан', + 1: 'Опорная точка', + 2: 'RACON (радиолокационный ответчик)', + 3: 'Стационарное морское сооружение', + 4: 'Аварийный буй обозначения затонувшего объекта', + 5: 'Огонь без секторов', + 6: 'Огонь с секторами', + 7: 'Передний створный огонь', + 8: 'Задний створный огонь', + 9: 'Знак: кардинальный северный', + 10: 'Знак: кардинальный восточный', + 11: 'Знак: кардинальный южный', + 12: 'Знак: кардинальный западный', + 13: 'Знак левой стороны фарватера', + 14: 'Знак правой стороны фарватера', + 15: 'Знак предпочтительного канала, левая сторона', + 16: 'Знак предпочтительного канала, правая сторона', + 17: 'Знак изолированной опасности', + 18: 'Знак чистой воды', + 19: 'Специальный знак', + 20: 'Плавучий кардинальный знак северный', + 21: 'Плавучий кардинальный знак восточный', + 22: 'Плавучий кардинальный знак южный', + 23: 'Плавучий кардинальный знак западный', + 24: 'Плавучий знак левой стороны', + 25: 'Плавучий знак правой стороны', + 26: 'Плавучий знак предпочтительного канала, левая сторона', + 27: 'Плавучий знак предпочтительного канала, правая сторона', + 28: 'Плавучий знак изолированной опасности', + 29: 'Плавучий знак чистой воды', + 30: 'Специальный плавучий знак', + 31: 'Плавучий маяк / LANBY / буровая установка', +}; + +function atonTypeLabel(code) { + if (code == null || code === '') return null; + const n = parseInt(code, 10); + if (!isNaN(n) && Object.prototype.hasOwnProperty.call(AIS_ATON_TYPE_LABELS, n)) { + return AIS_ATON_TYPE_LABELS[n]; + } + const s = String(code).trim(); + return s || null; +} + +// Infer vessel class from observed AIS message types. +function _vesselClassFromMsgTypes(msgTypes) { + if (!Array.isArray(msgTypes)) return null; + let sawA = false, sawB = false, sawBS = false, sawATON = false; + for (const t of msgTypes) { + const n = parseInt(t, 10); + if (n === 1 || n === 2 || n === 3 || n === 5) sawA = true; + else if (n === 18 || n === 19 || n === 24) sawB = true; + else if (n === 4 || n === 11) sawBS = true; + else if (n === 21) sawATON = true; + } + if (sawA) return 'A'; + if (sawB) return 'B'; + if (sawBS) return 'BS'; + if (sawATON) return 'N'; + return null; +} + +// MergedTarget (ais_hub) → наш вессел shape (shipname/course/speed/timestamp/…). +function mergedTargetToVessel(t) { + if (!t || typeof t !== 'object') return null; + const dyn = t.dynamic || {}; + const dims = t.dims || {}; + const voy = t.voyage || {}; + const sig = t.signal || {}; + const name = (t.name != null) ? String(t.name).trim() : null; + const callsign = (t.callsign != null) ? String(t.callsign).trim() : null; + const ts = t.last_seen != null ? t.last_seen + : (t.last_dynamic_ts || t.last_static_ts || 0); + return { + mmsi: t.mmsi, + shipname: name || null, + callsign: callsign || null, + imo: t.imo != null ? t.imo : null, + shiptype: t.ship_type != null ? t.ship_type : null, + vessel_class: _vesselClassFromMsgTypes(t.msg_types), + lat: dyn.lat != null ? dyn.lat : null, + lon: dyn.lon != null ? dyn.lon : null, + course: dyn.cog != null ? dyn.cog : null, + speed: dyn.sog != null ? dyn.sog : null, + heading: dyn.heading != null ? dyn.heading : null, + nav_status: dyn.nav_status != null ? dyn.nav_status : null, + rot: dyn.rot != null ? dyn.rot : null, + to_bow: dims.a != null ? dims.a : null, + to_stern: dims.b != null ? dims.b : null, + to_port: dims.c != null ? dims.c : null, + to_starboard: dims.d != null ? dims.d : null, + eta: voy.eta != null ? voy.eta : null, + draught: voy.draught != null ? voy.draught : null, + destination: voy.destination != null ? voy.destination : null, + signal_db: sig.last_db != null ? sig.last_db : null, + signal_ts: sig.last_ts != null ? sig.last_ts : null, + timestamp: ts ? Math.floor(ts) : 0, + last_seen: ts, + }; +} + +// ais_hub ownship (GET /api/v1/ownship / ownship.update) → наш shape. +function ownshipToLocal(o) { + if (!o || typeof o !== 'object') return null; + return { + lat: o.lat != null ? o.lat : null, + lon: o.lon != null ? o.lon : null, + course: o.cog != null ? o.cog : null, + speed: o.sog != null ? o.sog : null, + heading: null, + satellites: o.sats != null ? o.sats : null, + fix_quality: o.fix_quality != null ? o.fix_quality : null, + timestamp: o.ts != null ? Math.floor(o.ts) : null, + source: 'nmea', + }; +} + +// base_station.update → наш shape для маркера. +function baseStationToLocal(b) { + if (!b || typeof b !== 'object') return null; + return { + mmsi: b.mmsi, + lat: b.lat != null ? b.lat : null, + lon: b.lon != null ? b.lon : null, + virtual: !!(b.virtual || b.virtual_station), + synthetic: !!b.synthetic, + timestamp: b.ts != null ? Math.floor(b.ts) : (b.timestamp != null ? Math.floor(b.timestamp) : 0), + }; +} + +// aton.update → наш buoy shape. +function atonToLocal(a) { + if (!a || typeof a !== 'object') return null; + const dims = a.dims || {}; + const atonType = a.type != null ? a.type : (a.aton_type != null ? a.aton_type : (a.aid_type != null ? a.aid_type : null)); + return { + mmsi: a.mmsi, + lat: a.lat != null ? a.lat : null, + lon: a.lon != null ? a.lon : null, + name: a.name || null, + aton_type: atonType, + aton_type_label: atonTypeLabel(atonType), + virtual: !!(a.virtual || a.virtual_aid || a.virtual_aton), + synthetic: !!a.synthetic, + off_position: !!(a.off_position || a.off_pos), + to_bow: a.to_bow != null ? a.to_bow : (dims.a != null ? dims.a : (a.a != null ? a.a : null)), + to_stern: a.to_stern != null ? a.to_stern : (dims.b != null ? dims.b : (a.b != null ? a.b : null)), + to_port: a.to_port != null ? a.to_port : (dims.c != null ? dims.c : (a.c != null ? a.c : null)), + to_starboard: a.to_starboard != null ? a.to_starboard : (dims.d != null ? dims.d : (a.d != null ? a.d : null)), + timestamp: a.ts != null ? Math.floor(a.ts) : (a.timestamp != null ? Math.floor(a.timestamp) : 0), + }; +} + +// occupancy object (GET /api/v1/slots → data.occupancy[A|B]) or slots.update event. +function slotsOccupancyToLocal(s) { + if (!s || typeof s !== 'object') return null; + return { + utc_minute: s.utc_minute != null ? s.utc_minute : null, + total: s.slots_total != null ? s.slots_total : 2250, + occupied: s.occupied_count != null ? s.occupied_count : 0, + noise_floor_dbm: null, + threshold_dbm: null, + slot0_unix_ms: s.slot0_unix_ms != null ? s.slot0_unix_ms : null, + first_occupied_unix_ms: s.first_occupied_unix_ms != null ? s.first_occupied_unix_ms : null, + bitmap: s.bitmap_hex || null, + timestamp: s.ts != null ? Math.floor(s.ts) : Math.floor(Date.now() / 1000), + }; +} + +// slots.detail event or REST detail[A|B] — список занятых слотов с уровнем сигнала. +function slotsDetailToLocal(d) { + if (!d || typeof d !== 'object') return null; + const signals = {}; + const entries = Array.isArray(d.entries) ? d.entries : []; + for (const e of entries) { + if (!e || e.slot == null) continue; + signals[e.slot] = (e.level_db != null) ? Math.round(e.level_db * 10) / 10 : null; + } + return { + utc_minute: d.utc_minute != null ? d.utc_minute : null, + signals, + timestamp: d.ts != null ? Math.floor(d.ts) : Math.floor(Date.now() / 1000), + }; +} + +// Throttled redraw coordinator: несколько target.update'ов подряд → один redraw. +const AisHubRedraw = (function () { + let pending = { vessels: false, baseStations: false, atons: false, ownship: false, stats: false, slots: false }; + let timer = null; + function flush() { + timer = null; + const p = pending; + pending = { vessels: false, baseStations: false, atons: false, ownship: false, stats: false, slots: false }; + try { + if (p.ownship && typeof updateOwnShipDisplay === 'function') updateOwnShipDisplay(); + if (p.vessels && typeof updateVessels === 'function') updateVessels(); + if (p.baseStations && typeof updateBaseStations === 'function') updateBaseStations(); + if (p.atons && typeof updateBuoys === 'function') updateBuoys(); + if (p.stats && currentTab === 'stats' && typeof updateStats === 'function') updateStats(); + if (p.slots && typeof slotsOpen !== 'undefined' && slotsOpen && typeof updateSlots === 'function') updateSlots(); + if (typeof adjustSidebarHeight === 'function') adjustSidebarHeight(); + } catch (e) { try { console.error('[AISMap] redraw failed', e); } catch (_) {} } + } + return { + request(kind) { + if (kind && pending.hasOwnProperty(kind)) pending[kind] = true; + if (timer == null) timer = setTimeout(flush, 200); + }, + flushNow: flush, + }; +})(); + +// Обработка одного события от ais_hub (target.update / ownship.update / …). +function handleAisHubEvent(ev) { + if (!ev || typeof ev !== 'object') return; + const t = ev.type; + const d = ev.data; + AisHub.lastEventTs = Date.now(); + if (t === 'state.snapshot' && d) { + // Полная замена витрин. + AisHub.vessels.clear(); + if (Array.isArray(d.vessels)) { + for (const mt of d.vessels) { + const v = mergedTargetToVessel(mt); + if (v && v.mmsi != null) AisHub.vessels.set(v.mmsi, v); + } + } + AisHub.baseStations.clear(); + if (Array.isArray(d.base_stations)) { + for (const b of d.base_stations) { + const bs = baseStationToLocal(b); + if (bs && bs.mmsi != null) AisHub.baseStations.set(bs.mmsi, bs); + } + } + AisHub.atons.clear(); + if (Array.isArray(d.atons)) { + for (const a of d.atons) { + const an = atonToLocal(a); + if (an && an.mmsi != null) AisHub.atons.set(an.mmsi, an); + } + } + AisHub.ownship = d.ownship ? ownshipToLocal(d.ownship) : null; + AisHub.stats = d.stats || null; + if (d.slots) { + const occ = d.slots.occupancy || {}; + const det = d.slots.detail || {}; + AisHub.slots.A = occ.A ? slotsOccupancyToLocal(occ.A) : null; + AisHub.slots.B = occ.B ? slotsOccupancyToLocal(occ.B) : null; + AisHub.slotDetail.A = det.A ? slotsDetailToLocal(det.A) : null; + AisHub.slotDetail.B = det.B ? slotsDetailToLocal(det.B) : null; + } + AisHub.snapshotLoaded = true; + AisHubRedraw.request('vessels'); + AisHubRedraw.request('baseStations'); + AisHubRedraw.request('atons'); + AisHubRedraw.request('ownship'); + AisHubRedraw.request('stats'); + AisHubRedraw.request('slots'); + return; + } + if (t === 'target.update' && d) { + const v = mergedTargetToVessel(d); + if (v && v.mmsi != null) { + AisHub.vessels.set(v.mmsi, v); + AisHubRedraw.request('vessels'); + } + return; + } + if (t === 'ownship.update' && d) { + AisHub.ownship = ownshipToLocal(d); + AisHubRedraw.request('ownship'); + return; + } + if (t === 'base_station.update' && d) { + const bs = baseStationToLocal(d); + if (bs && bs.mmsi != null) { + AisHub.baseStations.set(bs.mmsi, bs); + AisHubRedraw.request('baseStations'); + } + return; + } + if (t === 'aton.update' && d) { + const an = atonToLocal(d); + if (an && an.mmsi != null) { + AisHub.atons.set(an.mmsi, an); + AisHubRedraw.request('atons'); + } + return; + } + if (t === 'stats.update' && d) { + AisHub.stats = d; + AisHubRedraw.request('stats'); + return; + } + if (t === 'slots.update' && d && (d.channel === 'A' || d.channel === 'B')) { + const ch = d.channel; + AisHub.slots[ch] = slotsOccupancyToLocal(d); + // Поддерживаем «per-minute» историю RSSI (для графиков), noise/threshold + // ais_hub нам не присылает → используем пустые плейсхолдеры, оставляем «ts». + const hist = AisHub.rssiHistory[ch]; + hist.push({ ts: (d.ts || Math.floor(Date.now() / 1000)), nf: null, th: null }); + if (hist.length > AIS_RSSI_HISTORY_MAX) hist.splice(0, hist.length - AIS_RSSI_HISTORY_MAX); + AisHubRedraw.request('slots'); + return; + } + if (t === 'slots.detail' && d && (d.channel === 'A' || d.channel === 'B')) { + const ch = d.channel; + AisHub.slotDetail[ch] = slotsDetailToLocal(d); + AisHubRedraw.request('slots'); + return; + } + if (t === 'signal.update' && d && (d.channel === 'A' || d.channel === 'B')) { + const ch = d.channel; + const events = Array.isArray(d.events) ? d.events : []; + const store = AisHub.signalEvents[ch]; + for (const e of events) { + if (!e) continue; + store.push({ + ts: e.unix_ms != null ? e.unix_ms / 1000 : Math.floor(Date.now() / 1000), + slot: e.slot != null ? e.slot : null, + mmsi: e.mmsi != null ? e.mmsi : null, + signal: e.level_db != null ? Math.round(e.level_db * 10) / 10 : null, + }); + } + if (store.length > AIS_SIGNAL_EVENTS_MAX) store.splice(0, store.length - AIS_SIGNAL_EVENTS_MAX); + AisHubRedraw.request('slots'); + return; + } + if (t === 'radio.update' && d) { + if (d.source === 'aiscatcher_rssi' || d.power_a_db != null || d.power_b_db != null) { + const ts = ev.ts || (Date.now() / 1000); + if (d.power_a_db != null) { + AisHub.livePower.A.push({ ts, power: Math.round(d.power_a_db * 10) / 10 }); + if (AisHub.livePower.A.length > AIS_LIVE_POWER_MAX) AisHub.livePower.A.shift(); + } + if (d.power_b_db != null) { + AisHub.livePower.B.push({ ts, power: Math.round(d.power_b_db * 10) / 10 }); + if (AisHub.livePower.B.length > AIS_LIVE_POWER_MAX) AisHub.livePower.B.shift(); + } + AisHubRedraw.request('slots'); + } + return; + } +} + +// WebSocket-клиент с переподключением (1→2→5→10 с). +const AisHubWS = (function () { + let ws = null; + let backoff = 1000; + let reconnectTimer = null; + function open() { + if (reconnectTimer != null) { clearTimeout(reconnectTimer); reconnectTimer = null; } + if (ws) { + try { + ws.onopen = ws.onclose = ws.onerror = ws.onmessage = null; + if (ws.readyState === WebSocket.CONNECTING || ws.readyState === WebSocket.OPEN) { + ws.close(1000, 'reconnect'); + } + } catch (_) {} + ws = null; + } + try { + const proto = (location.protocol === 'https:') ? 'wss:' : 'ws:'; + const url = proto + '//' + location.host + '/ws'; + ws = new WebSocket(url); + } catch (e) { + scheduleReconnect(); + return; + } + ws.onopen = () => { + AisHub.wsOpen = true; + backoff = 1000; + try { console.log('[AISMap] ws open'); } catch (_) {} + }; + ws.onmessage = (m) => { + let ev; + try { ev = JSON.parse(m.data); } catch (e) { return; } + handleAisHubEvent(ev); + }; + ws.onerror = () => {}; + ws.onclose = () => { + AisHub.wsOpen = false; + try { console.warn('[AISMap] ws close, reconnect in', backoff, 'ms'); } catch (_) {} + scheduleReconnect(); + }; + } + function scheduleReconnect() { + if (reconnectTimer != null) return; + reconnectTimer = setTimeout(() => { reconnectTimer = null; open(); }, backoff); + backoff = Math.min(backoff * 2, 10000); + } + return { open }; +})(); +const tabs = document.querySelectorAll('.nav-tab'); +const pages = document.querySelectorAll('.tab-page'); +const hamburger = document.getElementById('hamburger'); +const navTabs = document.getElementById('nav-tabs'); + +// ===================== VisualViewport safe insets (WebView/browser UI) ===================== +function updateVisualViewportInsets(){ + const vv = window.visualViewport; + if (!vv) return; + // Approximate occluded areas in CSS px + const top = Math.max(0, vv.offsetTop || 0); + const bottom = Math.max(0, (window.innerHeight - (vv.height + (vv.offsetTop || 0))) || 0); + const left = Math.max(0, vv.offsetLeft || 0); + const right = Math.max(0, (window.innerWidth - (vv.width + (vv.offsetLeft || 0))) || 0); + const root = document.documentElement; + root.style.setProperty('--vv-top', top.toFixed(0) + 'px'); + root.style.setProperty('--vv-bottom', bottom.toFixed(0) + 'px'); + root.style.setProperty('--vv-left', left.toFixed(0) + 'px'); + root.style.setProperty('--vv-right', right.toFixed(0) + 'px'); +} +try{ + if (window.visualViewport) { + window.visualViewport.addEventListener('resize', updateVisualViewportInsets); + window.visualViewport.addEventListener('scroll', updateVisualViewportInsets); + window.addEventListener('resize', updateVisualViewportInsets); + updateVisualViewportInsets(); + } +}catch(e){} + +// ===================== Session settings (cookie) ===================== +function _cookieOpts() { + const secure = (location && location.protocol === 'https:') ? '; Secure' : ''; + return '; Path=/; SameSite=Lax' + secure; +} +function cookieGet(name) { + try { + const key = encodeURIComponent(name) + '='; + const parts = String(document.cookie || '').split(/;\s*/); + for (const p of parts) { + if (p.startsWith(key)) return decodeURIComponent(p.slice(key.length)); + } + } catch (e) {} + return null; +} +function cookieSet(name, value) { + try { + document.cookie = encodeURIComponent(name) + '=' + encodeURIComponent(String(value)) + _cookieOpts(); + return true; + } catch (e) {} + return false; +} +const SSK = 'aismap_'; +function sGet(key, def) { + const v = cookieGet(SSK + key); + return (v == null || v === '') ? def : v; +} +function sSet(key, value) { + return cookieSet(SSK + key, value); +} + +function switchTab(tab) { + currentTab = tab; + tabs.forEach(t => t.classList.toggle('active', t.dataset.tab === tab)); + pages.forEach(p => p.classList.toggle('active', p.id === 'page-' + tab)); + navTabs.classList.remove('open'); + if (tab === 'map') { + setTimeout(() => map.invalidateSize(), 50); + } + if (tab === 'config') { + if (!cfgLoaded) loadConfig(); + loadServiceStatus(); + } + if (tab === 'console') { + initConsole().then(() => { + if (window._fitTerminal) setTimeout(() => window._fitTerminal(), 80); + }); + } + if (tab === 'transponder') { + loadTransponderPage(); + } + if (tab === 'targets') { + try { renderTargetsTab(); } catch (_) {} + } +} +tabs.forEach(t => t.addEventListener('click', () => switchTab(t.dataset.tab))); +hamburger.addEventListener('click', () => navTabs.classList.toggle('open')); + +// ===================== Transponder (Class B) ===================== +async function tpParseJsonResponse(r) { + const text = await r.text(); + if (!text || !text.trim()) { + return { _empty: true, _status: r.status }; + } + try { + return JSON.parse(text); + } catch (e) { + const snippet = text.replace(/\s+/g, ' ').slice(0, 180); + throw new Error('Ответ не JSON (HTTP ' + r.status + '): ' + snippet); + } +} + +function tpCollectConfig() { + return { + mmsi: parseInt(document.getElementById('tp-mmsi').value, 10) || 0, + shipname: document.getElementById('tp-shipname').value, + callsign: document.getElementById('tp-callsign').value, + ship_type: parseInt(document.getElementById('tp-ship-type').value, 10) || 0, + to_bow: parseInt(document.getElementById('tp-to-bow').value, 10) || 0, + to_stern: parseInt(document.getElementById('tp-to-stern').value, 10) || 0, + to_port: parseInt(document.getElementById('tp-to-port').value, 10) || 0, + to_starboard: parseInt(document.getElementById('tp-to-starboard').value, 10) || 0, + vendorid: document.getElementById('tp-vendorid').value, + model: parseInt(document.getElementById('tp-model').value, 10) || 0, + serial: parseInt(document.getElementById('tp-serial').value, 10) || 0, + use_gps_motion: document.getElementById('tp-use-gps').checked, + nrzi_slot_channel: document.getElementById('tp-slot-channel').value, + nrzi_slot: parseInt(document.getElementById('tp-slot-number').value, 10) || 0, + nrzi_encoder: document.getElementById('tp-nrzi-encoder').value, + nrzi_mode: document.getElementById('tp-nrzi-mode').value, + include_nrzi_preamble: document.getElementById('tp-preamble').checked, + nrzi_preamble_bits: Math.max(0, Math.min(128, parseInt(document.getElementById('tp-preamble-bits').value, 10) || 0)), + nrzi_pad_payload_to_octet: document.getElementById('tp-pad-payload').checked, + nrzi_pad_nrz_bits: Math.max(0, Math.min(4096, parseInt(document.getElementById('tp-pad-nrz-bits').value, 10) || 0)), + tx_gpio_pulse_auto: document.getElementById('tp-gpio-auto').checked, + tx_gpio_pulse_delay_ms: Math.max(50, Math.min(100, parseInt(document.getElementById('tp-gpio-delay-ms').value, 10) || 75)), + tx_gpio_pulse_script: document.getElementById('tp-gpio-script').value, + }; +} + +function tpApplyConfig(c) { + if (!c) return; + document.getElementById('tp-mmsi').value = c.mmsi != null ? c.mmsi : ''; + document.getElementById('tp-shipname').value = c.shipname || ''; + document.getElementById('tp-callsign').value = c.callsign || ''; + const stSel = document.getElementById('tp-ship-type'); + if (stSel) { + const stv = c.ship_type != null ? parseInt(c.ship_type, 10) : 0; + if (typeof window.ensureShipTypeOption === 'function') { + window.ensureShipTypeOption(Number.isFinite(stv) ? stv : 0); + } + stSel.value = String(Number.isFinite(stv) ? Math.max(0, Math.min(255, stv)) : 0); + } + document.getElementById('tp-to-bow').value = c.to_bow != null ? c.to_bow : ''; + document.getElementById('tp-to-stern').value = c.to_stern != null ? c.to_stern : ''; + document.getElementById('tp-to-port').value = c.to_port != null ? c.to_port : ''; + document.getElementById('tp-to-starboard').value = c.to_starboard != null ? c.to_starboard : ''; + document.getElementById('tp-vendorid').value = c.vendorid || ''; + document.getElementById('tp-model').value = c.model != null ? c.model : ''; + document.getElementById('tp-serial').value = c.serial != null ? c.serial : ''; + document.getElementById('tp-use-gps').checked = !!c.use_gps_motion; + const tch = document.getElementById('tp-slot-channel'); + if (tch) tch.value = (c.nrzi_slot_channel === 'B') ? 'B' : 'A'; + const tsn = document.getElementById('tp-slot-number'); + if (tsn) { + const sn = c.nrzi_slot != null ? Math.max(0, Math.min(2249, parseInt(c.nrzi_slot, 10) || 0)) : 0; + tsn.value = sn; + } + const enc = document.getElementById('tp-nrzi-encoder'); + if (enc) enc.value = (c.nrzi_encoder === 'ais_phy') ? 'ais_phy' : 'aistx'; + document.getElementById('tp-nrzi-mode').value = (c.nrzi_mode === 'expanded') ? 'expanded' : 'packed'; + document.getElementById('tp-preamble').checked = c.include_nrzi_preamble !== false; + document.getElementById('tp-pad-payload').checked = c.nrzi_pad_payload_to_octet !== false; + const tpn = document.getElementById('tp-pad-nrz-bits'); + if (tpn) { + const pn = c.nrzi_pad_nrz_bits != null ? parseInt(c.nrzi_pad_nrz_bits, 10) : 256; + tpn.value = isNaN(pn) ? 256 : Math.max(0, Math.min(4096, pn)); + } + const tpb = document.getElementById('tp-preamble-bits'); + if (tpb) { + const pb = c.nrzi_preamble_bits != null ? parseInt(c.nrzi_preamble_bits, 10) : 24; + tpb.value = isNaN(pb) ? 24 : Math.max(0, Math.min(128, pb)); + } + const gAuto = document.getElementById('tp-gpio-auto'); + if (gAuto) gAuto.checked = !!c.tx_gpio_pulse_auto; + const gDel = document.getElementById('tp-gpio-delay-ms'); + if (gDel) { + const gd = c.tx_gpio_pulse_delay_ms != null ? parseInt(c.tx_gpio_pulse_delay_ms, 10) : 75; + gDel.value = isNaN(gd) ? 75 : Math.max(50, Math.min(100, gd)); + } + const gScr = document.getElementById('tp-gpio-script'); + if (gScr) gScr.value = c.tx_gpio_pulse_script != null ? c.tx_gpio_pulse_script : ''; + if (typeof window.shipDimsEditorRefresh === 'function') window.shipDimsEditorRefresh(); +} + +function tpFormatSendResult(res) { + if (!res) return ''; + const parts = []; + if (res.dest) parts.push(res.dest); + if (res.slot_channel != null && res.slot != null) parts.push('ch ' + res.slot_channel + ' slot ' + res.slot); + if (res.nrzi_bytes) parts.push('UDP: ' + res.nrzi_bytes + ' B'); + if (res.payload_bytes != null) parts.push('NRZI payload: ' + res.payload_bytes + ' B'); + if (res.udp_bytes != null) parts.push('UDP кадр: ' + res.udp_bytes + ' B'); + if (res.gpio_pulse && res.gpio_pulse.length) { + const gp = res.gpio_pulse.map(function (p) { + if (p.ok) return 'ok'; + return 'fail: ' + (p.error || p.stderr || p.returncode); + }).join('; '); + parts.push('GPIO: ' + gp); + } + if (res.errors && res.errors.length) parts.push(res.errors.join('; ')); + return parts.join(' · '); +} + +function tpFormatPreview(prev) { + if (!prev || !prev.nrzi_hex) return ''; + const lines = []; + if (prev.dest) lines.push('Назначение UDP: ' + prev.dest); + if (prev.slot_channel != null && prev.slot != null) { + lines.push('Заголовок кадра: канал ' + prev.slot_channel + ', слот ' + prev.slot + ' (как тест слота)'); + } + const phy = prev.phy; + if (phy) { + lines.push('=== PHY (отладка) ==='); + lines.push('Преамбула: ' + (phy.include_preamble ? phy.preamble_bits + ' бит 1010… (как gr-aistx)' : 'выкл (только HDLC+NRZI)')); + lines.push('NRZI режим: ' + (phy.nrzi_mode || '')); + if (phy.fcs) lines.push('FCS: ' + phy.fcs); + if (phy.hdlc) lines.push('HDLC: ' + phy.hdlc); + if (phy.nrzi) lines.push('NRZI: ' + phy.nrzi); + if (phy.packed) lines.push('Packed: ' + phy.packed); + if (phy.nrzi_encoder) lines.push('Кодер: ' + phy.nrzi_encoder); + if (phy.aistx_phy_note) lines.push(phy.aistx_phy_note); + if (phy.pad_payload_to_octet != null) { + lines.push('Добор payload до октета: ' + (phy.pad_payload_to_octet ? 'да' : 'нет')); + } + if (phy.pad_nrz_total_bits != null && phy.pad_nrz_total_bits > 0) { + lines.push('Добор NRZ перед NRZI до: ' + phy.pad_nrz_total_bits + ' бит'); + } else if (phy.pad_nrz_total_bits === null || phy.pad_nrz_total_bits === 0) { + lines.push('Добор NRZ перед NRZI: выкл'); + } + const pm = phy.per_message || {}; + ['18', '19', '24A', '24B'].forEach(k => { + const s = pm[k]; + if (!s) return; + const p0 = s.payload_bits_input != null ? s.payload_bits_input : s.payload_bits; + const p1 = s.payload_bits_after_pad != null ? s.payload_bits_after_pad : p0; + const ptxt = p0 === p1 ? (p0 + ' бит') : (p0 + '→' + p1 + ' бит'); + const stuff = s.bits_between_flags_stuffed != null ? s.bits_between_flags_stuffed : '—'; + const hdlc = s.hdlc_frame_bits != null ? s.hdlc_frame_bits + ' бит' : (s.hdlc_note || '—'); + lines.push(k + ': payload ' + ptxt + ', stuff ' + stuff + ', HDLC ' + hdlc + + ', NRZI ' + s.nrzi_packed_bytes + ' B, UDP ' + s.udp_total_bytes + ' B'); + }); + if (phy.test_slot_note) lines.push(phy.test_slot_note); + } + lines.push('=== Только NRZI (hex) ==='); + const hx = prev.nrzi_hex || {}; + ['18', '19', '24A', '24B'].forEach(k => { + const h = hx[k]; + lines.push(k + ': ' + (h ? h.length + ' hex — ' + h.slice(0, 120) + (h.length > 120 ? '…' : '') : '-')); + }); + const fr = prev.udp_frame_hex || {}; + lines.push('=== Полный UDP payload: канал+слот+NRZI (hex) ==='); + ['18', '19', '24A', '24B'].forEach(k => { + const h = fr[k]; + lines.push(k + ': ' + (h ? h.length + ' hex — ' + h.slice(0, 140) + (h.length > 140 ? '…' : '') : '-')); + }); + return lines.join('\n'); +} + +function tpSetMsg(el, text, ok) { + if (!el) return; + el.textContent = text || ''; + el.classList.remove('ok', 'err'); + if (text) el.classList.add(ok ? 'ok' : 'err'); +} + +async function loadTransponderPage() { + const msg = document.getElementById('tp-msg'); + try { + const r = await fetch('/api/transponder'); + const d = await tpParseJsonResponse(r); + if (d._empty || d.ok === false) { + tpSetMsg(msg, d.error || 'Пустой ответ API', false); + return; + } + tpApplyConfig(d.config); + const encSel = document.getElementById('tp-nrzi-encoder'); + if (encSel) { + const ok = !!d.aistx_phy_available; + Array.from(encSel.options).forEach(function(o) { + if (o.value === 'aistx') o.disabled = !ok; + }); + if (!ok && encSel.value === 'aistx') encSel.value = 'ais_phy'; + } + const own = d.ownship || {}; + const hint = document.getElementById('tp-ownship-hint'); + if (hint) { + if (own.lat != null && own.lon != null) { + hint.textContent = own.lat.toFixed(5) + ', ' + own.lon.toFixed(5) + + ' | COG ' + (own.course != null ? own.course : '—') + + ' | SOG ' + (own.speed != null ? own.speed : '—'); + } else { + hint.textContent = 'Нет фикса'; + } + } + tpSetMsg(msg, '', true); + } catch (e) { + tpSetMsg(msg, 'Загрузка: ' + e.message, false); + } +} + +async function tpDoPreview() { + const out = document.getElementById('tp-preview-out'); + const msg = document.getElementById('tp-msg'); + const sn = parseInt(document.getElementById('tp-slot-number').value, 10); + if (isNaN(sn) || sn < 0 || sn > 2249) { + tpSetMsg(msg, 'Слот 0–2249', false); + return; + } + tpSetMsg(msg, 'Превью…', true); + try { + const r = await fetch('/api/transponder/preview', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(tpCollectConfig()), + }); + const d = await tpParseJsonResponse(r); + if (!d.ok) { + tpSetMsg(msg, d.error || 'Ошибка', false); + return; + } + out.textContent = tpFormatPreview(d.preview); + tpSetMsg(msg, 'Превью обновлено', true); + } catch (e) { + tpSetMsg(msg, e.message, false); + } +} + +async function tpDoSave() { + const msg = document.getElementById('tp-msg'); + try { + const r = await fetch('/api/transponder', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(tpCollectConfig()), + }); + const d = await tpParseJsonResponse(r); + if (!d.ok && d.error) { + tpSetMsg(msg, d.error, false); + return; + } + if (d.config) tpApplyConfig(d.config); + tpSetMsg(msg, 'Сохранено', true); + } catch (e) { + tpSetMsg(msg, e.message, false); + } +} + +async function tpDoGpioPulseOnce() { + const msg = document.getElementById('tp-msg'); + tpSetMsg(msg, 'Импульс GPIO…', true); + try { + const r = await fetch('/api/transponder/gpio_pulse', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(tpCollectConfig()), + }); + const d = await tpParseJsonResponse(r); + if (!d.ok) { + tpSetMsg(msg, d.error || 'Ошибка GPIO', false); + return; + } + const p = d.pulse || {}; + tpSetMsg(msg, 'GPIO: ok (code ' + (p.returncode != null ? p.returncode : 0) + ')', true); + } catch (e) { + tpSetMsg(msg, e.message, false); + } +} + +async function tpDoSend(which) { + const msg = document.getElementById('tp-msg'); + const sn = parseInt(document.getElementById('tp-slot-number').value, 10); + if (isNaN(sn) || sn < 0 || sn > 2249) { + tpSetMsg(msg, 'Слот 0–2249', false); + return; + } + const body = Object.assign({}, tpCollectConfig(), { which }); + tpSetMsg(msg, 'Отправка…', true); + try { + const r = await fetch('/api/transponder/send', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body), + }); + const d = await tpParseJsonResponse(r); + if (!d.ok) { + tpSetMsg(msg, d.error || 'Ошибка', false); + return; + } + const res = d.result || {}; + const line = tpFormatSendResult(res); + tpSetMsg(msg, line || 'Готово', !(res.errors && res.errors.length)); + } catch (e) { + tpSetMsg(msg, e.message, false); + } +} + +(function initTransponderUi() { + const save = document.getElementById('tp-save'); + const preview = document.getElementById('tp-preview'); + if (!save || !preview) return; + save.addEventListener('click', () => tpDoSave()); + preview.addEventListener('click', () => tpDoPreview()); + document.getElementById('tp-send-18').addEventListener('click', () => tpDoSend('18')); + document.getElementById('tp-send-19').addEventListener('click', () => tpDoSend('19')); + document.getElementById('tp-send-24a').addEventListener('click', () => tpDoSend('24A')); + document.getElementById('tp-send-24b').addEventListener('click', () => tpDoSend('24B')); + document.getElementById('tp-send-broadcast').addEventListener('click', () => tpDoSend('broadcast')); + const gpioOnce = document.getElementById('tp-gpio-pulse-once'); + if (gpioOnce) gpioOnce.addEventListener('click', () => tpDoGpioPulseOnce()); + const rawBtn = document.getElementById('tp-send-raw'); + if (rawBtn) rawBtn.addEventListener('click', () => tpDoSendRaw()); +})(); + +async function tpDoSendRaw() { + const msg = document.getElementById('tp-msg'); + const hex = document.getElementById('tp-raw-hex').value; + const channel = document.getElementById('tp-slot-channel').value; + const slot = parseInt(document.getElementById('tp-slot-number').value, 10); + if (isNaN(slot) || slot < 0 || slot > 2249) { + tpSetMsg(msg, 'Слот 0–2249', false); + return; + } + if (!hex.trim()) { + tpSetMsg(msg, 'Введите hex', false); + return; + } + tpSetMsg(msg, 'Отправка…', true); + try { + const r = await fetch('/api/transponder/send_raw', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(Object.assign({}, tpCollectConfig(), { nrzi_hex: hex, channel, slot })), + }); + const d = await tpParseJsonResponse(r); + if (!d.ok) { + tpSetMsg(msg, d.error || 'Ошибка', false); + return; + } + const res = d.result || {}; + const line = tpFormatSendResult(res); + tpSetMsg(msg, line || 'Готово', !(res.errors && res.errors.length)); + } catch (e) { + tpSetMsg(msg, e.message, false); + } +} + +function loadStylesheet(href) { + return new Promise((resolve, reject) => { + const l = document.createElement('link'); + l.rel = 'stylesheet'; + l.href = href; + l.onload = () => resolve(); + l.onerror = () => reject(new Error('css')); + document.head.appendChild(l); + }); +} +function loadScript(src) { + return new Promise((resolve, reject) => { + const s = document.createElement('script'); + s.src = src; + s.onload = resolve; + s.onerror = () => reject(new Error('script')); + document.head.appendChild(s); + }); +} + +let _consoleInitPromise = null; +function initConsole() { + if (window._consoleInited) { + if (window._fitTerminal) setTimeout(() => window._fitTerminal(), 80); + return Promise.resolve(); + } + if (_consoleInitPromise) return _consoleInitPromise; + _consoleInitPromise = doInitConsole(); + return _consoleInitPromise; +} + +async function doInitConsole() { + try { + const elUnavailable = document.getElementById('terminal-unavailable'); + const elWrap = document.getElementById('terminal-wrap'); + const elStatus = document.getElementById('console-status'); + let cfg; + try { + const r = await fetch('/api/terminal'); + cfg = await r.json(); + } catch (e) { + elUnavailable.style.display = 'block'; + elUnavailable.textContent = 'Не удалось получить настройки терминала.'; + elStatus.textContent = 'ошибка'; + return; + } + if (!cfg.pty) { + elUnavailable.style.display = 'block'; + elUnavailable.innerHTML = 'Интерактивная консоль доступна только на устройстве под Linux (PTY). На Windows shell в браузере не подключается.'; + elStatus.textContent = 'недоступно'; + window._consoleInited = true; + return; + } + if (!cfg.ws) { + elUnavailable.style.display = 'block'; + elUnavailable.textContent = 'Установите зависимость: pip install flask-sock'; + elStatus.textContent = 'нет WS'; + window._consoleInited = true; + return; + } + + try { + await loadStylesheet('/static/xterm/xterm.css'); + await loadScript('/static/xterm/xterm.min.js'); + await loadScript('/static/xterm/xterm-addon-fit.min.js'); + } catch (e) { + elUnavailable.style.display = 'block'; + elUnavailable.textContent = 'Не удалось загрузить xterm.js (проверьте сеть / CDN).'; + elStatus.textContent = 'ошибка'; + return; + } + + const Term = window.Terminal; + /* UMD xterm-addon-fit: window.FitAddon — это модуль { FitAddon: class }, не конструктор */ + const FitAddonMod = window.FitAddon; + const FitAddonCtor = (FitAddonMod && typeof FitAddonMod.FitAddon === 'function') + ? FitAddonMod.FitAddon + : (typeof FitAddonMod === 'function' ? FitAddonMod : null); + if (!Term || !FitAddonCtor) { + elUnavailable.style.display = 'block'; + elUnavailable.textContent = 'xterm: неверные глобалы после загрузки скриптов.'; + elStatus.textContent = 'ошибка'; + return; + } + + elUnavailable.style.display = 'none'; + elWrap.style.display = 'block'; + + const term = new Term({ + cursorBlink: true, + fontSize: 14, + fontFamily: "'JetBrains Mono', 'Cascadia Mono', 'Consolas', monospace", + theme: { + background: '#0d1117', + foreground: '#e0e0e0', + cursor: '#d2ff1a', + cursorAccent: '#1a1a2e', + selectionBackground: '#264f78' + } + }); + const fitAddon = new FitAddonCtor(); + term.loadAddon(fitAddon); + term.open(elWrap); + fitAddon.fit(); + + const proto = location.protocol === 'https:' ? 'wss:' : 'ws:'; + const ws = new WebSocket(proto + '//' + location.host + '/ws/terminal'); + ws.binaryType = 'arraybuffer'; + + ws.onopen = () => { + elStatus.textContent = 'подключено'; + ws.send(JSON.stringify({ type: 'resize', cols: term.cols, rows: term.rows })); + }; + ws.onclose = () => { elStatus.textContent = 'отключено'; }; + ws.onerror = () => { elStatus.textContent = 'ошибка соединения'; }; + + ws.onmessage = (ev) => { + const data = ev.data; + if (data instanceof ArrayBuffer) { + term.write(new Uint8Array(data)); + } else if (typeof Blob !== 'undefined' && data instanceof Blob) { + data.arrayBuffer().then(buf => term.write(new Uint8Array(buf))); + } else { + term.write(data); + } + }; + + term.onData(data => { + if (ws.readyState === WebSocket.OPEN) ws.send(data); + }); + term.onResize(({ cols, rows }) => { + if (ws.readyState === WebSocket.OPEN) { + ws.send(JSON.stringify({ type: 'resize', cols, rows })); + } + }); + + window._fitTerminal = () => { try { fitAddon.fit(); } catch(e) {} }; + window.addEventListener('resize', () => { + if (currentTab === 'console' && window._fitTerminal) window._fitTerminal(); + }); + + window._consoleInited = true; + } finally { + _consoleInitPromise = null; + } +} + +// ===================== Map init ===================== +L.Icon.Default.imagePath = '/static/leaflet/images/'; +const EMPTY_TILE = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=='; + +/** Текст внутри SVG (не HTML). */ +function _vtSvgEscapeText(s) { + return String(s) + .replace(/&/g, '&') + .replace(//g, '>'); +} + +// Векторная подложка в тон веб-UI: суша светлее, вода темнее — заметнее граница с фоном #0e1a2b +var _vtLand = '#323f58'; +var _vtWater = '#0c1424'; +var _vtWaterStroke = '#1a3d5c'; +var _vtCoastStroke = '#5c7fa3'; +var _vtWaterway = '#3d5d78'; +var _vtRoad = '#5a6578'; +var _vtBuilding = '#2c3548'; +var _vtGreen = '#2a3d34'; +var _vtPark = '#263d30'; +var _vtLabelFill = '#c9d1d9'; +var _vtLabelHalo = '#0e1a2b'; + +/** + * Leaflet.VectorGrid рисует подписи точек только через L.icon + iconUrl. + * Дубли по границам тайлов — ограничение MVT; уменьшаем шум: высокий minZoom, только нужные классы. + */ +function _vtTextLabelIcon(name, zoom, minZoom) { + if (zoom < minZoom || !name || typeof name !== 'string') return null; + const fs = zoom <= 7 ? 10 : zoom <= 10 ? 11 : 12; + const padX = 6; + // 0.52 занижало ширину для кириллицы / жирного шрифта → текст обрезался по viewBox SVG. + let nonAscii = 0; + for (let i = 0; i < name.length; i++) { + if (name.charCodeAt(i) > 127) nonAscii++; + } + const perChar = fs * (nonAscii > 0 ? 0.72 : 0.62); + const strokePad = 8; + const w = Math.min(640, Math.max(20, Math.ceil(name.length * perChar + padX * 2 + strokePad))); + const h = fs + 10; + const esc = _vtSvgEscapeText(name); + const svg = '' + + '' + esc + ''; + const url = 'data:image/svg+xml;charset=UTF-8,' + encodeURIComponent(svg); + return L.icon({ + iconUrl: url, + iconSize: [w, h], + iconAnchor: [0, h], + }); +} + +function _vtPlaceName(properties) { + if (!properties || typeof properties !== 'object') return ''; + return ( + properties.name || + properties.name_en || + properties['name:latin'] || + properties['name:en'] || + properties['name:ru'] || + properties['name:de'] || + '' + ); +} + +/** Только крупные населённые пункты (без деревень, кварталов, POI). */ +function _vtCityPlaceLabelIcon(properties, zoom) { + const cls = String(properties.class || '').toLowerCase(); + if (cls) { + if (cls !== 'city' && cls !== 'town') return null; + if (properties.rank != null && Number(properties.rank) > 10) return null; + return _vtTextLabelIcon(_vtPlaceName(properties), zoom, 7); + } + // Простые тайлы без class: только с z≥8, отсекаем типичные мелкие типы + if (zoom < 8) return null; + const sub = String(properties.subclass || properties.type || '').toLowerCase(); + if (['village', 'hamlet', 'suburb', 'quarter', 'neighbourhood', 'locality', 'isolated_dwelling'].indexOf(sub) >= 0) { + return null; + } + return _vtTextLabelIcon(_vtPlaceName(properties), zoom, 8); +} + +/** Подписи только рек / ручьёв / каналов (не озёра/моря). */ +function _vtRiverNameLabelIcon(properties, zoom) { + const cls = String(properties.class || '').toLowerCase(); + if (['river', 'stream', 'canal'].indexOf(cls) < 0) return null; + if (properties.rank != null && Number(properties.rank) > 4) return null; + return _vtTextLabelIcon(_vtPlaceName(properties), zoom, 10); +} + +function _vtWaterNameLayerStyle(p, z) { + const icon = _vtRiverNameLabelIcon(p, z); + if (!icon) return []; + return {icon: icon}; +} + +function _vtWaterwayLineStyle(p, z) { + var w = z < 6 ? 0.4 : z < 9 ? 0.65 : 0.95; + return {weight: w, color: _vtWaterway, opacity: 0.82, fill: false, fillOpacity: 0}; +} + +function _vtRoadLineStyle(p, z) { + var w = z < 6 ? 0.5 : z < 8 ? 0.85 : 1.15; + return {weight: w, color: _vtRoad, opacity: 0.7, fill: false, fillOpacity: 0}; +} + +/** Береговая линия (LineString). Несколько имён слоёв — у разных сборок planet/OSM по-разному. */ +function _vtCoastlineLineStyle(p, z) { + var w = z < 8 ? 0.85 : 1.15; + return { + fill: false, + fillOpacity: 0, + weight: w, + color: _vtCoastStroke, + opacity: 0.88, + lineCap: 'round', + lineJoin: 'round', + }; +} + +/** + * Слой water в planet-часто смешанный: Polygon + LineString + Point. + * Точки — в основном seamark (мосты, ограничения по высоте), паромы, slipway: дают «кружки цвета воды». + * В VectorGrid PointSymbolizer: _radius = style.radius || defaultRadius — при radius: 0 срабатывает дефолт ~10px. + */ +function _vtWaterLayerPointLikeProps(p) { + if (!p || typeof p !== 'object') return false; + if (p.amenity === 'ferry_terminal' || p.leisure === 'slipway' || p.railway === 'ferry_terminal' || p.ferry === 'yes') { + return true; + } + for (var k in p) { + if (Object.prototype.hasOwnProperty.call(p, k) && k.indexOf('seamark:') === 0) { + return true; + } + } + return false; +} + +function _vtWaterLayerStyle(properties) { + if (_vtWaterLayerPointLikeProps(properties)) { + return []; + } + return { + fill: true, + weight: 0.45, + color: _vtWaterStroke, + opacity: 0.85, + fillColor: _vtWater, + fillOpacity: 1, + radius: 0.01, + }; +} + +const map = L.map('map', { + background: '#0e1a2b', + maxZoom: 19, + // Critical for performance with many polylines/polygons at high zoom. + preferCanvas: true, + // leaflet-rotate plugin + rotate: true, + // leaflet-rotate.js enables this by default; we use our own compass UI. + rotateControl: false, + bearing: 0, +}).setView([55.751244, 37.618423], 10); + +// Put AIS/aux markers into a pane that does NOT rotate with the map. +// leaflet-rotate creates `norotatePane`; if not present we fall back to default markerPane. +let AIS_MARKER_PANE = null; +try { + const nr = map.getPane && map.getPane('norotatePane'); + if (nr) { + AIS_MARKER_PANE = 'aisMarkerPane'; + if (!map.getPane(AIS_MARKER_PANE)) { + map.createPane(AIS_MARKER_PANE, nr); + map.getPane(AIS_MARKER_PANE).style.zIndex = 610; // above tiles/overlays, under popups + } + } +} catch (e) { AIS_MARKER_PANE = null; } + +function _ensureAisMarkerPane(mk) { + try { + if (!AIS_MARKER_PANE || !mk || !mk.options || mk.options.pane === AIS_MARKER_PANE) return; + // Pane is effectively applied on add; re-add to move existing markers. + const wasOnMap = !!(map && map.hasLayer && map.hasLayer(mk)); + if (wasOnMap) mk.remove(); + mk.options.pane = AIS_MARKER_PANE; + if (wasOnMap) mk.addTo(map); + } catch (e) {} +} + +// ===================== Compass + map rotation ===================== +// Default is ON: the map rotates so the heading points up, which is the most +// useful mode on a vessel. Users can toggle it off via the compass button in +// #map-controls, the dial in #ownship-panel, or the HUD compass click. +let rotateMapByCompass = true; +try { + const _rmc = sGet('rotateMapByCompass', null); + if (_rmc != null) rotateMapByCompass = !!_rmc; +} catch (e) {} +let _lastOwnshipHeadingForRotate = null; +let _ownshipCompassUi = { wrap: null, btn: null, dial: null, arrow: null }; + +function _mapBearingDegSafe() { + try { return (map && typeof map.getBearing === 'function') ? (map.getBearing() || 0) : 0; } catch (e) { return 0; } +} + +function _applyMapRotation() { + try { + if (!map || typeof map.setBearing !== 'function') return; + const hd = (rotateMapByCompass && _lastOwnshipHeadingForRotate != null && !isNaN(_lastOwnshipHeadingForRotate)) + ? _lastOwnshipHeadingForRotate + : null; + // heading-up: rotate map counter to heading + map.setBearing(hd == null ? 0 : -hd); + } catch (e) {} +} + +function _setOwnshipCompassUi() { + const wrap = _ownshipCompassUi.wrap; + if (!wrap) return; + wrap.classList.toggle('compass--rotate-on', !!rotateMapByCompass); + const hd = (_lastOwnshipHeadingForRotate != null && !isNaN(_lastOwnshipHeadingForRotate)) ? _lastOwnshipHeadingForRotate : null; + // Compass indicates north: when map rotated by -hd, north appears at +hd on screen. + const a = (rotateMapByCompass && hd != null) ? hd : 0; + if (_ownshipCompassUi.arrow) _ownshipCompassUi.arrow.style.transform = 'rotate(' + (Math.round(a * 10) / 10) + 'deg)'; + if (_ownshipCompassUi.dial) _ownshipCompassUi.dial.classList.toggle('compass--no-heading', hd == null); +} + +// Shared toggle for "heading-up" (map rotates by compass). Used by: +// - #ownship-panel compass button + dial (desktop) +// - #map-controls #mc-compass (everywhere) +// - Clicking the HUD compass (#nhud-compass) as a quick shortcut +function _toggleRotateMapByCompass() { + rotateMapByCompass = !rotateMapByCompass; + try { sSet('rotateMapByCompass', rotateMapByCompass); } catch (e) {} + try { _setOwnshipCompassUi(); } catch (e) {} + try { _reflectCompassToggleUi(); } catch (e) {} + try { _applyMapRotation(); } catch (e) {} +} + +// Reflect the current state on the #map-controls compass button (active ring) +// and the HUD compass element (clickable cursor). +function _reflectCompassToggleUi() { + const mc = document.getElementById('mc-compass'); + if (mc) { + mc.classList.toggle('active', !!rotateMapByCompass); + mc.title = rotateMapByCompass + ? 'Heading-up: ВКЛ (нажмите, чтобы North-up)' + : 'North-up (нажмите, чтобы вращать карту по компасу)'; + } + const hc = document.getElementById('nhud-compass'); + if (hc) { + hc.classList.toggle('is-rotating-map', !!rotateMapByCompass); + } +} + +function _initOwnshipCompassUiOnce() { + try { + if (_ownshipCompassUi.wrap) return; + const btn = document.getElementById('btn-rotate-map'); + const dial = document.getElementById('os-compass-dial'); + const arrow = document.getElementById('os-compass-arrow'); + if (!btn || !dial || !arrow) return; + _ownshipCompassUi = { wrap: dial.closest('.os-compass') || dial, btn, dial, arrow }; + + btn.addEventListener('click', (e) => { e.preventDefault(); _toggleRotateMapByCompass(); }); + dial.addEventListener('dblclick', (e) => { e.preventDefault(); _toggleRotateMapByCompass(); }); + _setOwnshipCompassUi(); + } catch (e) {} +} +try { _initOwnshipCompassUiOnce(); } catch (e) {} + +// Initial UI reflection for controls that exist at page load. +document.addEventListener('DOMContentLoaded', () => { + try { _reflectCompassToggleUi(); } catch (e) {} +}); +// Also attach a click handler on the HUD compass once it's in the DOM. +document.addEventListener('DOMContentLoaded', () => { + const hc = document.getElementById('nhud-compass'); + if (hc && !hc.dataset.hcBound) { + hc.dataset.hcBound = '1'; + hc.style.cursor = 'pointer'; + hc.addEventListener('click', (e) => { e.preventDefault(); _toggleRotateMapByCompass(); }); + } +}); +// Apply initial rotation state right after map init (in case GPS already has a fix +// from a saved state or Android compass starts quickly). +try { setTimeout(() => { try { _applyMapRotation(); } catch (_) {} }, 100); } catch (e) {} + +// When the map bearing changes (programmatically or via gestures), keep ownship marker +// in sync even if no new GPS data arrived (e.g. during follow/compass updates). +try { + map.on('rotate', () => { + try { if (ownShipMarker) setOwnShipRotation(ownShipMarker, _lastOwnshipHeadingForRotate); } catch (e) {} + }); +} catch (e) {} + +const _vtBaseLayerStyles = { + // Локальные / planet_small слои (land, water, coastline, place) + land: {fill: true, weight: 0, fillColor: _vtLand, fillOpacity: 1}, + water: _vtWaterLayerStyle, + coastline: _vtCoastlineLineStyle, + coastlines: _vtCoastlineLineStyle, + coast_line: _vtCoastlineLineStyle, + ocean_coastline:_vtCoastlineLineStyle, + osm_coastline: _vtCoastlineLineStyle, + water_point: [], + ocean_point: [], + marine_point: [], + // place: function (properties, zoom) { + // const icon = _vtCityPlaceLabelIcon(properties, zoom); + // if (!icon) return []; + // return {icon: icon}; + // }, + place: [], + // Линии воды / дорог (явный stroke, без синего Leaflet по умолчанию) + waterway: _vtWaterwayLineStyle, + stream: _vtWaterwayLineStyle, + hydrology: _vtWaterwayLineStyle, + transportation: _vtRoadLineStyle, + road: _vtRoadLineStyle, + roads: _vtRoadLineStyle, + street: _vtRoadLineStyle, + streets: _vtRoadLineStyle, + highway: _vtRoadLineStyle, + bridge: _vtRoadLineStyle, + tunnel: _vtRoadLineStyle, + railway: _vtRoadLineStyle, + rail: _vtRoadLineStyle, + landcover: {fill: true, weight: 0, fillColor: _vtGreen, fillOpacity: 0.45}, + landuse: {fill: true, weight: 0, fillColor: _vtLand, fillOpacity: 0.5}, + park: {fill: true, weight: 0, fillColor: _vtPark, fillOpacity: 0.5}, + transportation_name: [], + // water_name: _vtWaterNameLayerStyle, + // waterway_name: _vtWaterNameLayerStyle, + water_name: [], + waterway_name: [], + building: {fill: true, weight: 0, fillColor: _vtBuilding, fillOpacity: 0.65}, + // boundary: function(p, z) { + // var admin = p.admin_level || 2; + // if (admin > 4) return []; + // return {weight: admin <= 2 ? 1.2 : 0.65, color: '#5c6b80', opacity: 0.45, dashArray: '6,3', fill: false, fillOpacity: 0}; + // }, + boundary: {weight: 1, color: '#5c6b80', opacity: 0.35, fill: false}, + housenumber: [], + poi: [], + mountain_peak: [], + aerodrome_label:[], + aeroway: {weight: 0.75, color: _vtRoad, opacity: 0.5, fill: false, fillOpacity: 0}, + globallandcover:{fill: true, weight: 0, fillColor: _vtGreen, fillOpacity: 0.35}, +}; + +const _vtLayerStyles = new Proxy(_vtBaseLayerStyles, { + get(target, prop) { + if (Object.prototype.hasOwnProperty.call(target, prop)) { + return target[prop]; + } + if (typeof prop !== 'string') { + return target[prop]; + } + // Слой есть в тайле, но нет в _vtBaseLayerStyles: не рисуем. + // Иначе точки превращаются в светлые CircleMarker («белые кружки» по всей карте). + return function () { + return []; + }; + }, +}); + +// Общие tweaks для всех TileLayer-ов: keepBuffer=4 — меньше просадок при pan'е, +// updateWhenZooming=false + updateWhenIdle=true — не шлём сотню запросов во время pinch-zoom. +const _tileTweaks = { + keepBuffer: 4, + updateWhenZooming: false, + updateWhenIdle: true, +}; + +const vectorLayer = L.vectorGrid.protobuf('/vtiles/{z}/{x}/{y}.pbf', Object.assign({}, _tileTweaks, { + maxZoom: 19, + // Локальные MVT сгенерированы до z13: при z>13 тот же растр тайла масштабируется (overzoom). + maxNativeZoom: 13, + rendererFactory: L.canvas.tile, + vectorTileLayerStyles: _vtLayerStyles, + interactive: false +})); + +const rasterLayer = L.tileLayer('/tiles/{z}/{x}/{y}.png', Object.assign({}, _tileTweaks, { + maxZoom: 19, + errorTileUrl: EMPTY_TILE +})); +vectorLayer.addTo(map); + +// Panes for z-index control (Leaflet default panes: overlayPane ~400, markerPane ~600) +try { + if (!map.getPane('vesselDetailPane')) { + map.createPane('vesselDetailPane'); + map.getPane('vesselDetailPane').style.zIndex = 650; // above overlays, below popups + } +} catch (e) {} + +// Shared Canvas renderer for vector overlays (trails/vectors/hulls/antenna). +// This avoids SVG DOM bloat and reduces input jank at high zoom. +const _overlayCanvasRenderer = L.canvas({ padding: 0.5 }); + +const osmLayer = L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', Object.assign({}, _tileTweaks, { + maxZoom: 19, + attribution: '© OpenStreetMap contributors', + crossOrigin: true +})); +const cartoPositron = L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', Object.assign({}, _tileTweaks, { + maxZoom: 20, + subdomains: 'abcd', + attribution: '© OpenStreetMap contributors © CARTO', + crossOrigin: true +})); +const cartoDark = L.tileLayer('https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png', Object.assign({}, _tileTweaks, { + maxZoom: 20, + subdomains: 'abcd', + attribution: '© OpenStreetMap contributors © CARTO', + crossOrigin: true +})); +const esriWorldImagery = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', Object.assign({}, _tileTweaks, { + maxZoom: 19, + attribution: 'Tiles © Esri', + crossOrigin: true +})); + +const baseLayers = { + 'Вектор': vectorLayer, + 'Локальные': rasterLayer, + 'OSM': osmLayer, + 'CARTO Light': cartoPositron, + 'CARTO Dark': cartoDark, + 'Esri Спутник': esriWorldImagery +}; +L.control.layers(baseLayers, null, {position: 'topright'}).addTo(map); + +function baseLayerNameByObj(layer) { + for (const [name, ly] of Object.entries(baseLayers)) { + if (ly === layer) return name; + } + return null; +} +function applyBaseLayerByName(name) { + const target = baseLayers[name]; + if (!target) return false; + for (const ly of Object.values(baseLayers)) { + if (map.hasLayer(ly)) map.removeLayer(ly); + } + target.addTo(map); + return true; +} +try { + const savedBase = sGet('baseLayer', ''); + if (savedBase && savedBase !== 'Вектор') applyBaseLayerByName(savedBase); +} catch (e) {} +map.on('baselayerchange', (e) => { + const nm = baseLayerNameByObj(e && e.layer); + if (nm) sSet('baseLayer', nm); +}); + +const vesselMarkers = new Map(); +const iconWidth = 17, iconHeight = 30; +const iconAnchorX = iconWidth / 2, iconAnchorY = iconHeight; + +// ===================== Base stations & buoys (separate layers) ===================== +const baseStationMarkers = new Map(); // mmsi -> marker +const buoyMarkers = new Map(); // mmsi -> marker +let lastBaseStations = []; +let lastBuoys = []; +let lastVisibleVessels = []; +let lastAnyVessels = []; + +function _mapGetByMmsi(src, mmsi) { + if (!src || mmsi == null) return null; + if (src.has(mmsi)) return src.get(mmsi); + const key = String(mmsi); + for (const [k, v] of src.entries()) { + if (String(k) === key) return v; + } + return null; +} + +function _markerForTargetMmsi(mmsi) { + return _mapGetByMmsi(vesselMarkers, mmsi) || + _mapGetByMmsi(baseStationMarkers, mmsi) || + _mapGetByMmsi(buoyMarkers, mmsi); +} + +function _decorateAisTarget(v) { + if (!v) return null; + const out = Object.assign({}, v); + if (out.vessel_class === 'BS' || out.kind === 'base_station') { + out.vessel_class = 'BS'; + out.kind = 'base_station'; + out.shipname = out.shipname || 'Базовая станция'; + out.virtual = !!out.virtual; + out.synthetic = !!out.synthetic; + } else if (out.vessel_class === 'N' || out.kind === 'buoy') { + out.vessel_class = 'N'; + out.kind = 'buoy'; + out.shipname = out.shipname || out.name || 'Буёк / СНО'; + out.aton_type_label = out.aton_type_label || atonTypeLabel(out.aton_type); + out.virtual = !!out.virtual; + out.synthetic = !!out.synthetic; + out.off_position = !!out.off_position; + } else { + out.kind = out.kind || 'vessel'; + } + const os = getOwnShipPos(); + if (os && out.lat != null && out.lon != null) { + out._distNM = haversineNM(os.lat, os.lon, out.lat, out.lon); + } + return out; +} + +function getAisTargetByMmsi(mmsi) { + const v = _mapGetByMmsi(vesselLastData, mmsi) || _mapGetByMmsi(AisHub.vessels, mmsi); + if (v) return _decorateAisTarget(Object.assign({ kind: 'vessel' }, v)); + const bs = _mapGetByMmsi(AisHub.baseStations, mmsi) || + (lastBaseStations || []).find(x => String(x && x.mmsi) === String(mmsi)); + if (bs) return _decorateAisTarget(Object.assign({ vessel_class: 'BS', kind: 'base_station' }, bs)); + const aton = _mapGetByMmsi(AisHub.atons, mmsi) || + (lastBuoys || []).find(x => String(x && x.mmsi) === String(mmsi)); + if (aton) return _decorateAisTarget(Object.assign({ vessel_class: 'N', kind: 'buoy' }, aton)); + return null; +} + +function _aidModeClass(item) { + if (item && item.virtual) return 'ais-aid--virtual'; + if (item && item.synthetic) return 'ais-aid--synthetic'; + return 'ais-aid--real'; +} + +function _aidModeLabel(item) { + if (item && item.virtual) return 'виртуальное'; + if (item && item.synthetic) return 'синтетическое'; + return 'реальное'; +} + +function _atonIconMeta(type) { + const c = parseInt(type, 10); + const fixed = c >= 5 && c <= 19; + const floating = c === 4 || (c >= 20 && c <= 31); + const nature = fixed ? 'fixed' : (floating ? 'floating' : 'generic'); + switch (c) { + case 2: return { style: 'racon', label: 'R', nature }; + case 3: return { style: 'structure', label: 'ПЛ', nature }; + case 4: return { style: 'wreck', label: 'АВ', nature }; + case 5: + case 6: return { style: 'light', label: 'ОГ', nature }; + case 7: return { style: 'leading', label: 'ПС', nature }; + case 8: return { style: 'leading', label: 'ЗС', nature }; + case 9: + case 20: return { style: 'cardinal-n', label: 'С', nature }; + case 10: + case 21: return { style: 'cardinal-e', label: 'В', nature }; + case 11: + case 22: return { style: 'cardinal-s', label: 'Ю', nature }; + case 12: + case 23: return { style: 'cardinal-w', label: 'З', nature }; + case 13: + case 24: return { style: 'lateral-port', label: 'Л', nature }; + case 14: + case 25: return { style: 'lateral-starboard', label: 'П', nature }; + case 15: + case 26: return { style: 'preferred-port', label: 'ЛК', nature }; + case 16: + case 27: return { style: 'preferred-starboard', label: 'ПК', nature }; + case 17: + case 28: return { style: 'isolated-danger', label: '!', nature }; + case 18: + case 29: return { style: 'safe-water', label: 'ЧВ', nature }; + case 19: + case 30: return { style: 'special', label: '*', nature }; + case 31: return { style: 'light-vessel', label: 'ПМ', nature }; + case 1: return { style: 'reference', label: 'ОТ', nature }; + default: return { style: 'generic', label: 'СНО', nature }; + } +} + +function getBaseStationDivIcon(b) { + const mode = _aidModeClass(b); + const label = _aidModeLabel(b); + const key = 'bs|' + mode; + const icon = L.divIcon({ + className: 'ais-bs-divicon ' + mode, + iconSize: [34, 34], + iconAnchor: [17, 17], + html: '
' + + '
', + }); + icon._aisIconKey = key; + return icon; +} + +function getBuoyDivIcon(b) { + const meta = _atonIconMeta(b && b.aton_type); + const mode = _aidModeClass(b); + const off = b && b.off_position ? ' ais-aid--offposition' : ''; + const key = 'aton|' + meta.style + '|' + meta.nature + '|' + mode + off; + const title = (b && (b.aton_type_label || atonTypeLabel(b.aton_type))) || 'Тип СНО не указан'; + const icon = L.divIcon({ + className: 'ais-aton-divicon ' + mode + off, + iconSize: [32, 32], + iconAnchor: [16, 16], + html: '
' + + '' + escHtml(meta.label) + '
', + }); + icon._aisIconKey = key; + return icon; +} + +function updateBaseStations() { + try { + const now = Math.floor(Date.now() / 1000); + const list = Array.from(AisHub.baseStations.values()) + .filter(b => !_isTargetExpiredByTimestamp(b, now)); + lastBaseStations = list.map(b => Object.assign({}, b, { + vessel_class: 'BS', + kind: 'base_station', + shipname: 'Базовая станция', + callsign: null, + shiptype: null, + virtual: !!b.virtual, + synthetic: !!b.synthetic, + })); + const visible = new Set(); + for (const b of list) { + if (!b || b.lat == null || b.lon == null) continue; + const mmsi = b.mmsi; + visible.add(mmsi); + if (baseStationMarkers.has(mmsi)) { + const mk = baseStationMarkers.get(mmsi); + _ensureAisMarkerPane(mk); + mk.setLatLng([b.lat, b.lon]); + const icon = getBaseStationDivIcon(b); + if (mk._aisIconKey !== icon._aisIconKey) { + mk.setIcon(icon); + mk._aisIconKey = icon._aisIconKey; + } + } else { + const icon = getBaseStationDivIcon(b); + const mk = L.marker([b.lat, b.lon], { icon, zIndexOffset: 500, pane: AIS_MARKER_PANE || undefined }).addTo(map); + mk._aisIconKey = icon._aisIconKey; + mk.on('click', () => { try { VesselInfoWindow.open(mmsi); } catch (_) {} }); + baseStationMarkers.set(mmsi, mk); + } + } + for (const [mmsi, mk] of baseStationMarkers.entries()) { + if (!visible.has(mmsi)) { + if (String(mmsi) === String(selectedMmsi)) selectedMmsi = null; + try { if (String(VesselInfoWindow.currentMmsi()) === String(mmsi)) VesselInfoWindow.close(); } catch (_) {} + map.removeLayer(mk); + baseStationMarkers.delete(mmsi); + } + } + for (const [mmsi, b] of AisHub.baseStations.entries()) { + if (_isTargetExpiredByTimestamp(b, now)) AisHub.baseStations.delete(mmsi); + } + } catch (e) { /* ignore */ } +} + +function updateBuoys() { + try { + const now = Math.floor(Date.now() / 1000); + const list = Array.from(AisHub.atons.values()) + .filter(b => !_isTargetExpiredByTimestamp(b, now)); + lastBuoys = list.map(b => Object.assign({}, b, { + vessel_class: 'N', + kind: 'buoy', + shipname: b && b.name ? b.name : 'Буёк / СНО', + aton_type_label: atonTypeLabel(b && b.aton_type), + callsign: null, + shiptype: null, + virtual: !!(b && b.virtual), + synthetic: !!(b && b.synthetic), + off_position: !!(b && b.off_position), + })); + const visible = new Set(); + for (const b of list) { + if (!b || b.lat == null || b.lon == null) continue; + const mmsi = b.mmsi; + visible.add(mmsi); + const typeLabel = atonTypeLabel(b.aton_type); + b.aton_type_label = typeLabel; + if (buoyMarkers.has(mmsi)) { + const mk = buoyMarkers.get(mmsi); + _ensureAisMarkerPane(mk); + mk.setLatLng([b.lat, b.lon]); + const icon = getBuoyDivIcon(b); + if (mk._aisIconKey !== icon._aisIconKey) { + mk.setIcon(icon); + mk._aisIconKey = icon._aisIconKey; + } + } else { + const icon = getBuoyDivIcon(b); + const mk = L.marker([b.lat, b.lon], { icon, zIndexOffset: 450, pane: AIS_MARKER_PANE || undefined }).addTo(map); + mk._aisIconKey = icon._aisIconKey; + mk.on('click', () => { try { VesselInfoWindow.open(mmsi); } catch (_) {} }); + buoyMarkers.set(mmsi, mk); + } + } + for (const [mmsi, mk] of buoyMarkers.entries()) { + if (!visible.has(mmsi)) { + if (String(mmsi) === String(selectedMmsi)) selectedMmsi = null; + try { if (String(VesselInfoWindow.currentMmsi()) === String(mmsi)) VesselInfoWindow.close(); } catch (_) {} + map.removeLayer(mk); + buoyMarkers.delete(mmsi); + } + } + for (const [mmsi, b] of AisHub.atons.entries()) { + if (_isTargetExpiredByTimestamp(b, now)) AisHub.atons.delete(mmsi); + } + } catch (e) { /* ignore */ } +} + +// ===================== Vessel motion overlays (vector + trail) ===================== +const VESSEL_PREDICT_SECONDS = 60; +const VESSEL_PREDICT_STEP_S = 5; +const VESSEL_TRAIL_KEEP_POINTS = 40; +const VESSEL_TRAIL_MIN_MOVE_M = 8; // reduce noise/jitter +const vesselVectors = new Map(); // mmsi -> L.Polyline +const vesselTrails = new Map(); // mmsi -> L.Polyline +const vesselHistory = new Map(); // mmsi -> Array<{lat:number, lon:number, ts:number}> +const vesselTrailRefMode = new Map(); // mmsi -> 'antenna' | 'center' + +function isFiniteNumber(x) { + return typeof x === 'number' && Number.isFinite(x); +} + +function destPointMeters(lat, lon, bearingDeg, distM) { + const R = 6371000; + const brng = bearingDeg * Math.PI / 180; + const φ1 = lat * Math.PI / 180, λ1 = lon * Math.PI / 180; + const δ = distM / R; + const sinφ1 = Math.sin(φ1), cosφ1 = Math.cos(φ1); + const sinδ = Math.sin(δ), cosδ = Math.cos(δ); + const sinφ2 = sinφ1 * cosδ + cosφ1 * sinδ * Math.cos(brng); + const φ2 = Math.asin(sinφ2); + const λ2 = λ1 + Math.atan2(Math.sin(brng) * sinδ * cosφ1, cosδ - sinφ1 * sinφ2); + return { lat: φ2 * 180 / Math.PI, lon: λ2 * 180 / Math.PI }; +} + +function haversineMetersLatLon(lat1, lon1, lat2, lon2) { + const R = 6371000; + const dLat = (lat2 - lat1) * Math.PI / 180; + const dLon = (lon2 - lon1) * Math.PI / 180; + const a = Math.sin(dLat/2)**2 + Math.cos(lat1*Math.PI/180)*Math.cos(lat2*Math.PI/180)*Math.sin(dLon/2)**2; + return 2 * R * Math.asin(Math.sqrt(a)); +} + +function rotToDegPerMin(rot) { + // We expect backend to provide ROT already in deg/min (AIS uses signed rate of turn). + // If it’s absent / not finite, treat as no turn. + if (!isFiniteNumber(rot)) return null; + return rot; +} + +function computePredictPath(lat, lon, courseDeg, speedKn, rotDegPerMin) { + if (!isFiniteNumber(lat) || !isFiniteNumber(lon)) return null; + if (!isFiniteNumber(courseDeg) || !isFiniteNumber(speedKn) || speedKn <= 0.05) return null; + + const stepS = Math.max(1, VESSEL_PREDICT_STEP_S); + const steps = Math.max(1, Math.floor(VESSEL_PREDICT_SECONDS / stepS)); + const metersPerSec = speedKn * 1852 / 3600; + const dStep = metersPerSec * stepS; + + let curLat = lat, curLon = lon; + const pts = [[curLat, curLon]]; + + const rot = rotToDegPerMin(rotDegPerMin); + const hasTurn = rot != null && Math.abs(rot) >= 0.2; // ignore tiny/noisy ROT + + for (let i = 1; i <= steps; i++) { + const tS = i * stepS; + const brg = hasTurn ? normalizeDeg(courseDeg + rot * (tS / 60)) : normalizeDeg(courseDeg); + const next = destPointMeters(curLat, curLon, brg, dStep); + curLat = next.lat; + curLon = next.lon; + pts.push([curLat, curLon]); + } + return pts; +} + +function getOrCreatePolyline(store, mmsi, opts) { + let pl = store.get(mmsi); + if (pl) return pl; + pl = L.polyline([], Object.assign({ renderer: _overlayCanvasRenderer }, opts)).addTo(map); + store.set(mmsi, pl); + return pl; +} + +function removeMotionOverlays(mmsi) { + const v = vesselVectors.get(mmsi); + if (v) { map.removeLayer(v); vesselVectors.delete(mmsi); } + const t = vesselTrails.get(mmsi); + if (t) { map.removeLayer(t); vesselTrails.delete(mmsi); } + vesselHistory.delete(mmsi); + vesselTrailRefMode.delete(mmsi); +} + +function updateVesselTrail(mmsi, lat, lon, ts) { + if (!isFiniteNumber(lat) || !isFiniteNumber(lon)) return; + const nowTs = isFiniteNumber(ts) ? ts : Math.floor(Date.now() / 1000); + let hist = vesselHistory.get(mmsi); + if (!hist) { hist = []; vesselHistory.set(mmsi, hist); } + + const last = hist.length ? hist[hist.length - 1] : null; + if (!last) { + hist.push({ lat, lon, ts: nowTs }); + } else { + const movedM = haversineMetersLatLon(last.lat, last.lon, lat, lon); + if (movedM >= VESSEL_TRAIL_MIN_MOVE_M) { + hist.push({ lat, lon, ts: nowTs }); + } else { + // If we didn’t move enough, still refresh timestamp to prevent “stale” tail decisions later. + last.ts = nowTs; + } + } + if (hist.length > VESSEL_TRAIL_KEEP_POINTS) hist.splice(0, hist.length - VESSEL_TRAIL_KEEP_POINTS); + + const pl = getOrCreatePolyline(vesselTrails, mmsi, { + color: '#4fc3f7', + weight: 2, + opacity: 0.65, + dashArray: '4 8', + lineCap: 'round', + interactive: false, + }); + pl.setLatLngs(hist.map(p => [p.lat, p.lon])); +} + +function updateVesselVector(mmsi, lat, lon, courseDeg, speedKn, rotDegPerMin) { + const path = computePredictPath(lat, lon, courseDeg, speedKn, rotDegPerMin); + if (!path) { + const pl = vesselVectors.get(mmsi); + if (pl) pl.setLatLngs([]); + return; + } + const pl = getOrCreatePolyline(vesselVectors, mmsi, { + color: '#d2ff1a', + weight: 2, + opacity: 0.9, + dashArray: null, + lineCap: 'round', + interactive: false, + }); + pl.setLatLngs(path); +} + +// ===================== Vessel detail overlays (true size hull + antenna point) ===================== +const VESSEL_DETAIL_ZOOM_MIN = 15; +const vesselLastData = new Map(); // mmsi -> last vessel object +const vesselHulls = new Map(); // mmsi -> L.Polygon +const vesselAntennas = new Map(); // mmsi -> L.CircleMarker + +function _numOrNull(v) { + if (v == null) return null; + const n = (typeof v === 'number') ? v : parseFloat(v); + return Number.isFinite(n) ? n : null; +} + +function _dimsFromVessel(v) { + const toBow = _numOrNull(v.to_bow); + const toStern = _numOrNull(v.to_stern); + const toPort = _numOrNull(v.to_port); + const toStar = _numOrNull(v.to_starboard); + if (![toBow, toStern, toPort, toStar].every(x => x != null && x > 0)) return null; + const lengthM = toBow + toStern; + const beamM = toPort + toStar; + if (!(lengthM > 0 && beamM > 0)) return null; + return { toBow, toStern, toPort, toStar, lengthM, beamM }; +} + +function _bearingForHull(v) { + const h = _numOrNull(v.heading); + const c = _numOrNull(v.course); + const s = _numOrNull(v.speed); + // Smart: when moving, COG is what user expects for rotation; when slow/stationary, heading is better. + const moving = s != null && !isNaN(s) && s >= 1.5; + const brg = moving ? (c != null ? c : h) : (h != null ? h : c); + return normalizeDeg(brg != null ? brg : null); +} + +function _centerlineFromAntenna(lat, lon, bearingDeg, dims) { + // Antenna can be offset from centerline. Positive is starboard. + const offStarM = (dims.toStar - dims.toPort) / 2; + return destPointMeters(lat, lon, normalizeDeg(bearingDeg + 90), offStarM); +} + +function _centerFromAntenna(lat, lon, bearingDeg, dims) { + // Center of hull relative to antenna: account for both longitudinal and lateral offset. + const cl = _centerlineFromAntenna(lat, lon, bearingDeg, dims); + const offFwdM = (dims.toBow - dims.toStern) / 2; + return destPointMeters(cl.lat, cl.lon, bearingDeg, offFwdM); +} + +function _hullPolygonFromAntenna(lat, lon, bearingDeg, dims, vesselClass) { + // AIS position is the GNSS antenna reference point. + // Build a simple "ship" polygon (with bow) in meters around that point. + const cl = _centerlineFromAntenna(lat, lon, bearingDeg, dims); + const halfBeam = (dims.toPort + dims.toStar) / 2; + const bowTip = destPointMeters(cl.lat, cl.lon, bearingDeg, dims.toBow); + const sternC = destPointMeters(cl.lat, cl.lon, normalizeDeg(bearingDeg + 180), dims.toStern); + const portBear = normalizeDeg(bearingDeg - 90); + const starBear = normalizeDeg(bearingDeg + 90); + + // For Class B at high zoom: simple triangle (tip + stern corners). + if (String(vesselClass || '').toUpperCase() === 'B') { + const sternStar = destPointMeters(sternC.lat, sternC.lon, starBear, halfBeam); + const sternPort = destPointMeters(sternC.lat, sternC.lon, portBear, halfBeam); + return [ + [bowTip.lat, bowTip.lon], + [sternStar.lat, sternStar.lon], + [sternPort.lat, sternPort.lon], + ]; + } + + // Bow shoulders: slightly behind the tip to form a pentagon (nose). + const minNose = 3.0; + const maxNose = Math.min(dims.toBow * 0.9, Math.max(6.0, dims.lengthM * 0.22)); + const noseLen = Math.max(minNose, Math.min(maxNose, dims.toBow * 0.65)); + const shoulderDist = Math.max(0, dims.toBow - noseLen); + const shoulderC = destPointMeters(cl.lat, cl.lon, bearingDeg, shoulderDist); + const bowPort = destPointMeters(shoulderC.lat, shoulderC.lon, portBear, halfBeam); + const bowStar = destPointMeters(shoulderC.lat, shoulderC.lon, starBear, halfBeam); + const sternStar = destPointMeters(sternC.lat, sternC.lon, starBear, halfBeam); + const sternPort = destPointMeters(sternC.lat, sternC.lon, portBear, halfBeam); + return [ + [bowTip.lat, bowTip.lon], + [bowStar.lat, bowStar.lon], + [sternStar.lat, sternStar.lon], + [sternPort.lat, sternPort.lon], + [bowPort.lat, bowPort.lon], + ]; +} + +function _ensureHull(mmsi, shiptype) { + let p = vesselHulls.get(mmsi); + if (p) return p; + p = L.polygon([], { + pane: 'vesselDetailPane', + renderer: _overlayCanvasRenderer, + color: '#000', + weight: 2, + opacity: 0.9, + fill: true, + fillColor: shiptypeToFillRgba(shiptype, null), + fillOpacity: 0.38, + interactive: false, + }).addTo(map); + vesselHulls.set(mmsi, p); + return p; +} + +function _ensureAntenna(mmsi) { + let c = vesselAntennas.get(mmsi); + if (c) return c; + c = L.circleMarker([0, 0], { + pane: 'vesselDetailPane', + renderer: _overlayCanvasRenderer, + radius: 3.5, + color: '#d2ff1a', + weight: 2, + opacity: 1, + fillColor: '#d2ff1a', + fillOpacity: 0.9, + interactive: false, + }).addTo(map); + vesselAntennas.set(mmsi, c); + return c; +} + +function removeVesselDetailOverlays(mmsi) { + const h = vesselHulls.get(mmsi); + if (h) { map.removeLayer(h); vesselHulls.delete(mmsi); } + const a = vesselAntennas.get(mmsi); + if (a) { map.removeLayer(a); vesselAntennas.delete(mmsi); } +} + +function updateVesselDetailOverlays(v) { + if (!v) return; + const mmsi = v.mmsi; + const lat = _numOrNull(v.lat); + const lon = _numOrNull(v.lon); + const z = map.getZoom(); + const mk = vesselMarkers.get(mmsi); + if (lat == null || lon == null || !mk) { removeVesselDetailOverlays(mmsi); return; } + + if (z < VESSEL_DETAIL_ZOOM_MIN) { + removeVesselDetailOverlays(mmsi); + mk.setOpacity(1); + return; + } + + const dims = _dimsFromVessel(v); + const brg = _bearingForHull(v); + if (!dims || brg == null) { + removeVesselDetailOverlays(mmsi); + mk.setOpacity(1); + return; + } + + const poly = _hullPolygonFromAntenna(lat, lon, brg, dims, v.vessel_class); + const hull = _ensureHull(mmsi, v.shiptype); + hull.setLatLngs(poly); + try { hull.setStyle({ fillColor: shiptypeToFillRgba(v.shiptype, v.nav_status) }); } catch (_) {} + const ant = _ensureAntenna(mmsi); + ant.setLatLng([lat, lon]); + + // Hide the simple marker when detailed overlay is visible. + mk.setOpacity(0); +} + +function _rgbaFromHex(hex, alpha) { + const s = String(hex || '').replace('#', ''); + const r = parseInt(s.slice(0, 2), 16); + const g = parseInt(s.slice(2, 4), 16); + const b = parseInt(s.slice(4, 6), 16); + return 'rgba(' + r + ',' + g + ',' + b + ',' + alpha + ')'; +} + +function vesselDisplayStyle(shiptype, navStatus) { + const c = parseInt(shiptype, 10); + const ns = parseInt(navStatus, 10); + const anchored = ns === 1 || ns === 5; + const restricted = ns === 3; + let color = '#9aa0a6'; + let stroke = '#111111'; + let label = 'Не указан'; + if (restricted) { + color = '#ff3045'; + stroke = '#111111'; + label = 'Ограниченная маневренность'; + } else if (!isNaN(c)) { + if (c === 30) { color = '#ff3045'; label = 'Рыболовное'; } + else if (c === 37) { color = '#d000ff'; label = 'Прогулочное'; } + else if (c >= 40 && c <= 49) { color = '#ffd31a'; stroke = '#7a5a00'; label = 'Высокоскоростное'; } + else if (c >= 50 && c <= 59) { color = '#00d7df'; stroke = '#005a60'; label = 'Буксир / спецсудно'; } + else if (c >= 60 && c <= 69) { color = '#3026df'; label = 'Пассажирское'; } + else if (c >= 70 && c <= 79) { color = '#19a64a'; label = 'Грузовое'; } + else if (c >= 80 && c <= 89) { color = '#e60018'; label = 'Танкер'; } + else if (c >= 90 && c <= 99) { color = '#f7f7f7'; stroke = '#111111'; label = 'Прочее'; } + else if (c >= 20 && c <= 29) { color = '#f7f7f7'; stroke = '#111111'; label = 'WIG / экраноплан'; } + else if (c >= 1 && c <= 19) { color = '#f7f7f7'; stroke = '#111111'; label = 'Зарезервировано'; } + else if (c >= 31 && c <= 36) { color = '#f7f7f7'; stroke = '#111111'; label = 'Тип 31-36'; } + else if (c >= 38 && c <= 39) { color = '#f7f7f7'; stroke = '#111111'; label = 'Зарезервировано'; } + else if (c >= 100) { color = '#f7f7f7'; stroke = '#111111'; label = 'Тип 100+'; } + } + return { color, stroke, anchored, restricted, label }; +} + +/** Полупрозрачная заливка по OpenCPN-подобным цветам ship type. */ +function shiptypeToFillRgba(shiptype, navStatus) { + return _rgbaFromHex(vesselDisplayStyle(shiptype, navStatus).color, 0.38); +} + +const _vesselSvgVB = { A: '0 0 88.5 152.73', B: '0 0 91.38 162.6' }; +const _vesselSvgPoly = { + A: '44.25 6.77 4.5 51.46 4.5 148.23 84 148.23 84 51.46 44.25 6.77', + B: '45.69 16.63 5.94 158.1 85.44 158.1 45.69 16.63', +}; +const vesselDivIconCache = new Map(); + +function vesselIconTintKey(vesselClass, shiptype, navStatus) { + const st = shiptype != null && shiptype !== '' ? String(shiptype) : '0'; + const ns = navStatus != null && navStatus !== '' ? String(navStatus) : ''; + return (vesselClass === 'A' ? 'A' : 'B') + ':' + st + ':' + ns; +} + +function getVesselDivIcon(vesselClass, shiptype, navStatus) { + const key = vesselIconTintKey(vesselClass, shiptype, navStatus); + let icon = vesselDivIconCache.get(key); + if (icon) return icon; + const isA = vesselClass === 'A'; + const vb = isA ? _vesselSvgVB.A : _vesselSvgVB.B; + const poly = isA ? _vesselSvgPoly.A : _vesselSvgPoly.B; + const style = vesselDisplayStyle(shiptype, navStatus); + if (style.restricted) { + const html = ''; + icon = L.divIcon({ html, iconSize: [18, 18], iconAnchor: [9, 9], popupAnchor: [0, -9], className: 'vessel-icon vessel-icon--svg vessel-icon--status' }); + } else if (style.anchored) { + const html = ''; + icon = L.divIcon({ html, iconSize: [18, 18], iconAnchor: [9, 9], popupAnchor: [0, -9], className: 'vessel-icon vessel-icon--svg vessel-icon--anchored' }); + } else { + const html = ''; + icon = L.divIcon({ + html, + iconSize: [iconWidth, iconHeight], + iconAnchor: [iconAnchorX, iconAnchorY], + popupAnchor: [0, -iconHeight], + className: 'vessel-icon vessel-icon--svg', + }); + } + vesselDivIconCache.set(key, icon); + return icon; +} + +function setIconRotation(marker, heading) { + if (!marker) return; + try { _ensureAisMarkerPane(marker); } catch (e) {} + // Store "true" heading for redraws. + marker._headingDeg = (heading != null && !isNaN(heading)) ? normalizeDeg(heading) : null; + + // Prefer leaflet-rotate's native per-marker rotation (radians). + // This avoids CSS-transform stacking issues during zoom/rotate. + try { + if (map && typeof map.getBearing === 'function' && marker.options) { + marker.options.rotateWithView = false; + const deg = (marker._headingDeg == null ? 0 : marker._headingDeg); + const headingRad = deg * Math.PI / 180; + // Markers are in `norotatePane` (do not rotate with the map), so keeping absolute heading + // relative to true north is simply "rotation = heading". + if (typeof marker.setRotation === 'function') marker.setRotation(headingRad); + else { + marker.options.rotation = headingRad; + if (typeof marker.update === 'function') marker.update(); + } + + // If this marker was previously patched by our CSS-rotate fallback, undo it. + const el = marker._icon; + if (marker._origSetPos) { + try { marker._setPos = marker._origSetPos; } catch (e) {} + try { delete marker._origSetPos; } catch (e) {} + } + if (el && el.dataset && el.dataset.trSet) { + try { delete el.dataset.trSet; } catch (e) {} + } + marker._rotationHeading = null; + return; + } + } catch (e) {} + + // Fallback (no rotate plugin): manual CSS rotation for the icon. + const el = marker._icon; + if (!el) return; + marker._rotationHeading = marker._headingDeg; + if (!el.dataset.trSet) { + el.style.transformOrigin = `${iconAnchorX}px ${iconAnchorY}px`; + el.dataset.trSet = '1'; + if (!marker._origSetPos) { + marker._origSetPos = marker._setPos; + marker._setPos = function(pos) { + this._origSetPos.call(this, pos); + const ic = this._icon; + if (ic && this._rotationHeading != null) { + const m = (ic.style.transform||'').match(/translate3d\([^)]+\)/); + if (m) ic.style.transform = m[0]+' rotate('+this._rotationHeading+'deg)'; + } + }; + } + } + const t = (el.style.transform||'').match(/translate3d\([^)]+\)/); + if (t) el.style.transform = marker._rotationHeading != null ? t[0]+' rotate('+marker._rotationHeading+'deg)' : t[0]; + else if (marker._rotationHeading != null) el.style.transform = 'rotate('+marker._rotationHeading+'deg)'; +} + +function _clampInt(v, minV, maxV, defV) { + const n = parseInt(v, 10); + if (!isFinite(n) || isNaN(n)) return defV; + return Math.max(minV, Math.min(maxV, n)); +} + +function _formatMinutesPreview(mins) { + const m = _clampInt(mins, 1, 24 * 60, 1); + if (m < 60) return m + ' мин'; + const h = Math.floor(m / 60); + const mm = m % 60; + return h + ' ч' + (mm ? (' ' + mm + ' мин') : ''); +} + +function getLosingTargetMinutes() { + return _clampInt(sGet('losingTargetMin', '7'), 1, 24 * 60, 7); +} +function getRemoveTargetMinutes() { + return _clampInt(sGet('removeTargetMin', '10'), 1, 24 * 60, 10); +} + +let LOSING_TARGET_TIME = getLosingTargetMinutes() * 60; +let REMOVE_TARGET_TIME = getRemoveTargetMinutes() * 60; + +function _isTargetExpiredByTimestamp(item, now) { + return !!(item && item.timestamp && (now - item.timestamp) >= REMOVE_TARGET_TIME); +} + +function applyTargetTimingFromSettings() { + let losingMin = getLosingTargetMinutes(); + let removeMin = getRemoveTargetMinutes(); + + // Ensure ordering: "losing" should be earlier than "remove" + if (removeMin <= losingMin) removeMin = losingMin + 1; + + LOSING_TARGET_TIME = losingMin * 60; + REMOVE_TARGET_TIME = removeMin * 60; + + const losingInput = document.getElementById('set-losing-target-min'); + const removeInput = document.getElementById('set-remove-target-min'); + const losingPrev = document.getElementById('set-losing-target-preview'); + const removePrev = document.getElementById('set-remove-target-preview'); + + if (losingInput) losingInput.value = String(losingMin); + if (removeInput) removeInput.value = String(removeMin); + if (losingPrev) losingPrev.textContent = _formatMinutesPreview(losingMin); + if (removePrev) removePrev.textContent = _formatMinutesPreview(removeMin); +} + +function initTargetTimingSettingsUi() { + const losingInput = document.getElementById('set-losing-target-min'); + const removeInput = document.getElementById('set-remove-target-min'); + if (!losingInput || !removeInput) return; + + const save = () => { + const losingMin = _clampInt(losingInput.value, 1, 24 * 60, 7); + const removeMinRaw = _clampInt(removeInput.value, 1, 24 * 60, 10); + const removeMin = Math.max(removeMinRaw, losingMin + 1); + sSet('losingTargetMin', String(losingMin)); + sSet('removeTargetMin', String(removeMin)); + applyTargetTimingFromSettings(); + }; + + losingInput.addEventListener('change', save); + losingInput.addEventListener('input', () => { + const prev = document.getElementById('set-losing-target-preview'); + if (prev) prev.textContent = _formatMinutesPreview(losingInput.value); + }); + removeInput.addEventListener('change', save); + removeInput.addEventListener('input', () => { + const prev = document.getElementById('set-remove-target-preview'); + if (prev) prev.textContent = _formatMinutesPreview(removeInput.value); + }); + + applyTargetTimingFromSettings(); +} + +try { + initTargetTimingSettingsUi(); + if (document && document.addEventListener) { + document.addEventListener('DOMContentLoaded', () => { + try { initTargetTimingSettingsUi(); } catch (e) {} + }); + } +} catch (e) {} + +// ===================== Distance & range filter ===================== +const RANGE_STEPS_NM = [0.1,0.2,0.3,0.5,0.7,1,1.5,2,3,5,7,10,15,20,30,50,70,100,150,200,300,500]; +const NM_TO_KM = 1.852; +const NM_TO_AU = 1.852 / 1.496e8; +const KN_TO_KMH = 1.852; +let distUnit = sGet('distUnit', 'nm'); +let speedUnit = sGet('speedUnit', 'kn'); +let rangeIdx = parseInt(sGet('rangeIdx', String(RANGE_STEPS_NM.length)), 10); +let rangeCircle = null; +let warnRadiusNm = parseFloat(sGet('warnRadiusNm', '0')) || 0; +let nearRadiusNm = parseFloat(sGet('nearRadiusNm', '0')) || 0; +let warnCircle = null; +let nearCircle = null; +let _dangerState = { any: false, count: 0, minNm: null }; + +function haversineNM(lat1, lon1, lat2, lon2) { + const R = 3440.065; + const dLat = (lat2 - lat1) * Math.PI / 180; + const dLon = (lon2 - lon1) * Math.PI / 180; + const a = Math.sin(dLat/2)**2 + Math.cos(lat1*Math.PI/180)*Math.cos(lat2*Math.PI/180)*Math.sin(dLon/2)**2; + return 2 * R * Math.asin(Math.sqrt(a)); +} + +function getOwnShipPos() { + const data = ownShipSource === 'phone' ? phoneGps : nmeaGps; + if (data && data.lat != null && data.lon != null) return data; + return null; +} + +function getRangeNM() { + return rangeIdx >= RANGE_STEPS_NM.length ? Infinity : RANGE_STEPS_NM[rangeIdx]; +} + +function fmtDist(nm) { + if (distUnit === 'au') { + const au = nm * NM_TO_AU; + return au.toExponential(2) + ' AU'; + } + if (distUnit === 'km') { + const km = nm * NM_TO_KM; + return km < 10 ? km.toFixed(2) + ' км' : km.toFixed(1) + ' км'; + } + return nm < 10 ? nm.toFixed(2) + ' NM' : nm.toFixed(1) + ' NM'; +} + +function bearingDeg(lat1, lon1, lat2, lon2) { + // Initial bearing from point 1 to point 2 (degrees from North, 0..360) + const φ1 = lat1 * Math.PI / 180, φ2 = lat2 * Math.PI / 180; + const Δλ = (lon2 - lon1) * Math.PI / 180; + const y = Math.sin(Δλ) * Math.cos(φ2); + const x = Math.cos(φ1) * Math.sin(φ2) - Math.sin(φ1) * Math.cos(φ2) * Math.cos(Δλ); + const θ = Math.atan2(y, x) * 180 / Math.PI; + return normalizeDeg(θ); +} + +function relBearingSignedDeg(ownHeadingDeg, absBearingDeg) { + const h = normalizeDeg(ownHeadingDeg); + const b = normalizeDeg(absBearingDeg); + if (h == null || b == null) return null; + let d = b - h; + if (d > 180) d -= 360; + if (d < -180) d += 360; + return d; // negative = left, positive = right +} + +function uiUnitName() { + return distUnit === 'km' ? 'км' : 'NM'; +} +function nmToUi(nm) { + return distUnit === 'km' ? (nm * NM_TO_KM) : nm; +} +function uiToNm(v) { + return distUnit === 'km' ? (v / NM_TO_KM) : v; +} + +function fmtRange(nm) { + if (!isFinite(nm)) return '\u221e'; + if (distUnit === 'au') { + const au = nm * NM_TO_AU; + return au.toExponential(2) + ' AU'; + } + if (distUnit === 'km') { + const km = nm * NM_TO_KM; + return (km < 1 ? km.toFixed(2) : km < 10 ? km.toFixed(1) : Math.round(km)) + ' км'; + } + return (nm < 1 ? nm.toFixed(1) : nm < 10 ? nm.toFixed(1) : Math.round(nm)) + ' NM'; +} + +function fmtSpeed(knots) { + if (speedUnit === 'kmh') return (knots * KN_TO_KMH).toFixed(1) + ' км/ч'; + return knots.toFixed(1) + ' уз.'; +} + +function updateRangeCircle() { + const os = getOwnShipPos(); + const r = getRangeNM(); + if (!os || !isFinite(r)) { + if (rangeCircle) { map.removeLayer(rangeCircle); rangeCircle = null; } + return; + } + const meters = r * 1852; + if (!rangeCircle) { + rangeCircle = L.circle([os.lat, os.lon], { + radius: meters, color: '#d2ff1a', weight: 1.5, + fillColor: '#d2ff1a', fillOpacity: 0.04, dashArray: '6,4', interactive: false + }).addTo(map); + } else { + rangeCircle.setLatLng([os.lat, os.lon]); + rangeCircle.setRadius(meters); + } +} + +function updateDangerCircles() { + const os = getOwnShipPos(); + if (!os || os.lat == null || os.lon == null) { + if (warnCircle) { map.removeLayer(warnCircle); warnCircle = null; } + if (nearCircle) { map.removeLayer(nearCircle); nearCircle = null; } + return; + } + + if (warnRadiusNm > 0) { + const meters = warnRadiusNm * 1852; + if (!warnCircle) { + warnCircle = L.circle([os.lat, os.lon], { + radius: meters, + color: '#f85149', + weight: 1.8, + opacity: 0.9, + fillColor: '#f85149', + fillOpacity: 0.03, + dashArray: '8,6', + interactive: false + }).addTo(map); + } else { + warnCircle.setLatLng([os.lat, os.lon]); + warnCircle.setRadius(meters); + } + } else if (warnCircle) { map.removeLayer(warnCircle); warnCircle = null; } + + if (nearRadiusNm > 0) { + const meters = nearRadiusNm * 1852; + if (!nearCircle) { + nearCircle = L.circle([os.lat, os.lon], { + radius: meters, + color: '#f0883e', + weight: 1.5, + opacity: 0.8, + fillColor: '#f0883e', + fillOpacity: 0.02, + dashArray: '4,8', + interactive: false + }).addTo(map); + } else { + nearCircle.setLatLng([os.lat, os.lon]); + nearCircle.setRadius(meters); + } + } else if (nearCircle) { map.removeLayer(nearCircle); nearCircle = null; } +} + +function updateDangerBanner() { + const el = document.getElementById('danger-banner'); + if (!el) return; + if (warnRadiusNm > 0 && _dangerState.any) { + const rel = _dangerState.relDegSigned; + const relTxt = (rel == null) ? '' : (rel < 0 ? (' ← ' + Math.round(Math.abs(rel)) + '°') : (' ' + Math.round(Math.abs(rel)) + '° →')); + const distTxt = _dangerState.minNm != null ? fmtDist(_dangerState.minNm) : ''; + el.textContent = 'ВНИМАНИЕ' + (_dangerState.count > 0 ? ' (' + _dangerState.count + (relTxt ? ',' + relTxt : '') + (distTxt ? ', ' + distTxt : '') + ')' : ''); + el.style.display = ''; + } else { + el.style.display = 'none'; + } +} + +(function initRangeSlider() { + const slider = document.getElementById('range-slider'); + const label = document.getElementById('range-value'); + slider.max = RANGE_STEPS_NM.length; + slider.value = rangeIdx; + label.textContent = fmtRange(getRangeNM()); + slider.addEventListener('input', () => { + rangeIdx = parseInt(slider.value, 10); + sSet('rangeIdx', rangeIdx); + label.textContent = fmtRange(getRangeNM()); + updateRangeCircle(); + }); +})(); +const olSize = Math.max(iconWidth, iconHeight)*1.5, olAnchor = olSize/2; +const iconChosen = L.icon({ iconUrl:'/svg/ChosenTarget.svg', iconSize:[olSize,olSize], iconAnchor:[olAnchor,olAnchor], className:'vessel-overlay-icon' }); +const iconLosing = L.divIcon({ + html: '', + iconSize: [18, 18], + iconAnchor: [9, 9], + className: 'vessel-overlay-icon vessel-overlay-lost', +}); +const vesselOverlays = new Map(); +let selectedMmsi = null; + +// ===================== Vessel InfoWindow (MarineTraffic-style) ===================== +// Replaces the plain Leaflet popup used previously. +// - Desktop: floating card, draggable by its header, anchored inside #map page. +// - Mobile : full-width bottom-sheet above the mobile panel bar, swipe-down closes it. +// - Stays on screen when the map pans/zooms (no snapping to marker after the user +// dragged it), but the content auto-updates when fresh AIS data arrives. +const AIS_NAV_STATUS_LABEL = { + 0: 'На ходу (двигатель)', 1: 'На якоре', 2: 'Не под командованием', + 3: 'Огранич. манёвренность', 4: 'Огранич. осадкой', 5: 'Пришвартован', + 6: 'На мели', 7: 'Рыболовство', 8: 'Под парусом', + 9: 'Рез. HSC', 10: 'Рез. WIG', 11: 'Буксир за кормой', + 12: 'Буксир спереди', 13: 'Резерв', 14: 'AIS-SART / MOB', 15: 'Не определён', +}; +function _navStatusLabel(c) { + if (c == null || c === '') return null; + const n = parseInt(c, 10); + if (isNaN(n)) return null; + return AIS_NAV_STATUS_LABEL[n] || ('Код ' + n); +} +function _signalDbToBars(db) { + if (db == null) return 0; + if (db >= -50) return 4; + if (db >= -60) return 3; + if (db >= -70) return 2; + if (db >= -80) return 1; + return 0; +} +function _vesselHeaderIconSvg(vesselClass, shiptype) { + const isA = vesselClass === 'A'; + const vb = isA ? _vesselSvgVB.A : _vesselSvgVB.B; + const poly = isA ? _vesselSvgPoly.A : _vesselSvgPoly.B; + const fill = shiptypeToFillRgba(shiptype, null); + return ''; +} + +const VesselInfoWindow = (function () { + let rootEl = null; + let openMmsi = null; + let userPositioned = false; + let _drag = null; + let isCollapsed = false; + + function el() { + if (rootEl) return rootEl; + rootEl = document.getElementById('vessel-infowindow'); + return rootEl; + } + function isMobile() { + try { + return window.innerWidth <= 600 || + (window.matchMedia && window.matchMedia('(max-height:520px) and (pointer:coarse)').matches); + } catch (e) { return false; } + } + + function _bars(bars) { + const heights = [5, 8, 11, 14]; + const cls = bars >= 3 ? 'on' : (bars === 2 ? 'warn' : (bars >= 1 ? 'bad' : '')); + let h = ''; + for (let i = 0; i < 4; i++) { + const active = i < bars; + h += ''; + } + h += ''; + return h; + } + + function _targetHeaderIconSvg(v) { + if (v && (v.vessel_class === 'BS' || v.kind === 'base_station')) { + return ''; + } + if (v && (v.vessel_class === 'N' || v.kind === 'buoy')) { + return ''; + } + return _vesselHeaderIconSvg(v ? v.vessel_class : null, v ? v.shiptype : null); + } + + function _dimsLabel(v) { + const toBow = (typeof v.to_bow === 'number' && isFinite(v.to_bow)) ? v.to_bow : parseInt(v.to_bow, 10); + const toStern = (typeof v.to_stern === 'number' && isFinite(v.to_stern)) ? v.to_stern : parseInt(v.to_stern, 10); + const toPort = (typeof v.to_port === 'number' && isFinite(v.to_port)) ? v.to_port : parseInt(v.to_port, 10); + const toStar = (typeof v.to_starboard === 'number' && isFinite(v.to_starboard)) ? v.to_starboard : parseInt(v.to_starboard, 10); + const lenM = (isFinite(toBow) ? Math.max(0, toBow) : 0) + (isFinite(toStern) ? Math.max(0, toStern) : 0); + const beamM = (isFinite(toPort) ? Math.max(0, toPort) : 0) + (isFinite(toStar) ? Math.max(0, toStar) : 0); + return (lenM > 0 && beamM > 0) ? (lenM + '×' + beamM + ' м') : null; + } + + function _buildStaticHtml(v) { + const isBase = v.vessel_class === 'BS' || v.kind === 'base_station'; + const isAton = v.vessel_class === 'N' || v.kind === 'buoy'; + const iconHtml = _targetHeaderIconSvg(v); + const iso2 = mmsiToIso2FromMid(v.mmsi); + const flag = iso2ToFlagEmoji(iso2); + const typeLabel = isAton ? (v.aton_type_label || atonTypeLabel(v.aton_type) || 'Тип СНО не указан') : 'Базовая станция AIS'; + const modeLabel = _aidModeLabel(v); + const clsText = isBase ? 'Базовая станция' : 'СНО / буй'; + const title = isBase ? 'Базовая станция' : (v.shipname || v.name || 'Буёк / СНО'); + + const os = getOwnShipPos(); + let bearingCell = ''; + if (os && os.lat != null && os.lon != null && v.lat != null && v.lon != null) { + const brg = bearingDeg(os.lat, os.lon, v.lat, v.lon); + const dist = v._distNM != null ? v._distNM : haversineNM(os.lat, os.lon, v.lat, v.lon); + bearingCell = '
' + + '
Пеленг / Дальность
' + + '
' + brg.toFixed(0) + '° / ' + escHtml(fmtDist(dist)) + '
'; + } + + const coordsTxt = (v.lat != null && v.lon != null) + ? Number(v.lat).toFixed(5) + ', ' + Number(v.lon).toFixed(5) + : '—'; + const dims = _dimsLabel(v); + const ageTxt = v.timestamp ? escHtml(fmtAgo(v.timestamp)) + ' назад' : '—'; + const miniDst = v._distNM != null ? escHtml(fmtDist(v._distNM)) : null; + const idsParts = ['MMSI: ' + escHtml(v.mmsi) + '']; + if (v.lat != null && v.lon != null) idsParts.push('Координаты: ' + escHtml(coordsTxt) + ''); + if (isAton) idsParts.push('СНО: ' + escHtml(modeLabel) + ''); + else idsParts.push('Станция: ' + escHtml(modeLabel) + ''); + if (isAton && v.off_position) idsParts.push('Положение: вне штатной позиции'); + if (dims) idsParts.push('Размер: ' + escHtml(dims) + ''); + + return '' + + '' + + '
' + + '
' + iconHtml + '
' + + '
' + + '
' + + (flag ? '' + flag + ' ' : '') + + escHtml(title) + + '
' + + '
' + escHtml(typeLabel) + ' · ' + escHtml(clsText) + '
' + + '
' + + (miniDst ? 'ДАЛЬН ' + miniDst + '' : '') + + 'ВОЗР ' + ageTxt + '' + + '
' + + '
' + + '' + + '' + + '
' + + '
' + + '
' + + '
Объект ' + escHtml(clsText) + '
' + + '
' + + '
' + escHtml(typeLabel) + '
' + + '
' + + 'Обновлено: ' + ageTxt + '' + + 'Источник: AIS' + + '
' + + '
' + + '
' + + '
' + (isAton ? 'Тип СНО' : 'Тип объекта') + '
' + escHtml(typeLabel) + '
' + + '
Координаты
' + escHtml(coordsTxt) + '
' + + '
' + (isAton ? 'Флаг СНО' : 'Флаг станции') + '
' + escHtml(modeLabel) + '
' + + (isAton && v.off_position ? '
Положение
Вне позиции
' : '') + + (dims ? '
Размер
' + escHtml(dims) + '
' : '') + + bearingCell + + '
' + + '
' + idsParts.join('') + '
' + + '
' + + '' + + '' + + '' + + '
' + + '
' + + ''; + } + + function _buildHtml(v) { + if (v && (v.vessel_class === 'BS' || v.vessel_class === 'N' || v.kind === 'base_station' || v.kind === 'buoy')) { + return _buildStaticHtml(v); + } + const iconHtml = _targetHeaderIconSvg(v); + const iso2 = mmsiToIso2FromMid(v.mmsi); + const flag = iso2ToFlagEmoji(iso2); + const typeLabel = (v.shiptype != null && v.shiptype !== '') + ? ((typeof AIS_TABLE51_SHIP_TYPE_LABEL === 'function' && AIS_TABLE51_SHIP_TYPE_LABEL(v.shiptype)) || ('Тип ' + v.shiptype)) + : 'Тип не указан'; + const clsText = v.vessel_class === 'A' ? 'Class A' + : v.vessel_class === 'B' ? 'Class B' + : v.vessel_class === 'BS' ? 'Базовая ст.' + : v.vessel_class === 'N' ? 'СНО / буй' + : (v.vessel_class || '?'); + + // Bearing / distance from ownship + const os = getOwnShipPos(); + let bearingCell = ''; + if (os && os.lat != null && os.lon != null && v.lat != null && v.lon != null) { + const brg = bearingDeg(os.lat, os.lon, v.lat, v.lon); + const dist = v._distNM; + bearingCell = '
' + + '
Пеленг / Дальность
' + + '
' + brg.toFixed(0) + '° / ' + + (dist != null ? escHtml(fmtDist(dist)) : '–') + '
'; + } + + const navLbl = _navStatusLabel(v.nav_status); + const navVal = navLbl || 'нет'; + const speedCourse = (v.speed != null ? escHtml(fmtSpeed(v.speed)) : '–') + + ' / ' + (v.course != null ? v.course.toFixed(0) + '°' : '–'); + const heading = v.heading != null ? v.heading.toFixed(0) + '°' + : 'нет'; + const draughtRaw = v.draught != null ? Number(v.draught) : null; + const draught = (draughtRaw != null && isFinite(draughtRaw) && draughtRaw > 0) + ? (draughtRaw.toFixed(1) + ' м') + : ''; + const dest = v.destination && String(v.destination).trim() + ? escHtml(String(v.destination).trim()) + : 'не указано'; + const eta = v.eta ? escHtml(String(v.eta)) : '—'; + + // Signal + let sigBlock = ''; + if (v.signal_db != null) { + const bars = _signalDbToBars(v.signal_db); + const ageTxt = v.signal_ts != null ? fmtAgo(Math.floor(v.signal_ts)) : null; + sigBlock = '
' + _bars(bars) + + 'Сигнал: ' + Number(v.signal_db).toFixed(1) + ' дБ' + + (ageTxt ? '(' + ageTxt + ' назад)' : '') + + '
'; + } + + // Identifiers + const callsign = v.callsign ? String(v.callsign).trim() : null; + const imo = v.imo && Number(v.imo) > 0 ? v.imo : null; + const idsParts = ['MMSI: ' + v.mmsi + '']; + if (imo) idsParts.push('IMO: ' + imo + ''); + if (callsign) idsParts.push('Позывной: ' + escHtml(callsign) + ''); + if (v.lat != null && v.lon != null) { + idsParts.push('Lat/Lon: ' + Number(v.lat).toFixed(5) + ', ' + Number(v.lon).toFixed(5) + ''); + } + if (v.rot != null && !isNaN(Number(v.rot))) { + idsParts.push('ROT: ' + Number(v.rot).toFixed(1) + ' °/мин'); + } + const lenM = (Number(v.to_bow) || 0) + (Number(v.to_stern) || 0); + const beamM = (Number(v.to_port) || 0) + (Number(v.to_starboard) || 0); + if (lenM > 0 && beamM > 0) { + idsParts.push('Размер: ' + lenM + '×' + beamM + ' м'); + } + + const receivedLine = v.timestamp + ? 'Получено: ' + escHtml(fmtTime(v.timestamp)) + ' (' + escHtml(fmtAgo(v.timestamp)) + ' назад)' + : 'Получено: '; + + // Mini stats for the collapsed state (shown instead of vinf-sub when collapsed) + const miniSog = v.speed != null ? escHtml(fmtSpeed(v.speed)) : '—'; + const miniCog = v.course != null ? v.course.toFixed(0) + '°' : '—'; + const miniDst = v._distNM != null ? escHtml(fmtDist(v._distNM)) : null; + + return '' + + '' + + '
' + + '
' + iconHtml + '
' + + '
' + + '
' + + (flag ? '' + flag + ' ' : '') + + escHtml(v.shipname || ('MMSI ' + v.mmsi)) + + '
' + + '
' + escHtml(typeLabel) + ' · ' + clsText + '
' + + '
' + + 'SOG ' + miniSog + '' + + 'COG ' + miniCog + '' + + (miniDst ? 'DST ' + miniDst + '' : '') + + '
' + + '
' + + '' + + '' + + '
' + + '
' + + '
' + + '
Назначение ' + dest + '
' + + '
' + + '
' + (eta !== '—' ? eta : '') + '
' + + '
' + + 'Обновлено: ' + (v.timestamp ? escHtml(fmtAgo(v.timestamp)) + ' назад' : '—') + '' + + 'ETA: ' + eta + '' + + '
' + + '
' + + '
' + + '
Навигационный статус
' + navVal + '
' + + '
Скорость / Курс (COG)
' + speedCourse + '
' + + '
Направление (HDG)
' + heading + '
' + + '
Осадка
' + draught + '
' + + bearingCell + + '
' + + sigBlock + + '
' + idsParts.join('') + '
' + + '
' + + '' + + '' + + '' + + '
' + + '
' + + ''; + } + + function _positionNearMarker() { + const root = el(); + if (!root || openMmsi == null) return; + if (isMobile()) { + root.style.left = ''; + root.style.top = ''; + root.style.right = ''; + root.style.bottom = ''; + return; + } + try { + const mk = _markerForTargetMmsi(openMmsi); + if (!mk) return; + const pt = map.latLngToContainerPoint(mk.getLatLng()); + const mapRect = map.getContainer().getBoundingClientRect(); + const w = root.offsetWidth || 360; + const h = root.offsetHeight || 280; + const margin = 12; + let x = pt.x + 28; + if (x + w + margin > mapRect.width) x = pt.x - w - 28; + if (x < margin) x = margin; + let y = pt.y - h / 2; + if (y < 56) y = 56; + if (y + h > mapRect.height - margin) y = Math.max(margin, mapRect.height - margin - h); + root.style.left = Math.round(x) + 'px'; + root.style.top = Math.round(y) + 'px'; + root.style.right = ''; + root.style.bottom = ''; + } catch (e) {} + } + + // Compute the map's "visible" sub-rectangle (excluding the infowindow occlusion) + // and pan so that the marker ends up in the centre of that visible area. + // Only shifts the map if the marker is (or would be) occluded. + function _panMarkerIntoView(latlng, opts) { + opts = opts || {}; + const root = el(); + if (!root || root.hidden || !latlng) return; + try { + const mapEl = map.getContainer(); + const mapRect = mapEl.getBoundingClientRect(); + const r = root.getBoundingClientRect(); + let vx0 = 0, vy0 = 0, vx1 = mapRect.width, vy1 = mapRect.height; + const ix0 = Math.max(0, r.left - mapRect.left); + const ix1 = Math.min(mapRect.width, r.right - mapRect.left); + const iy0 = Math.max(0, r.top - mapRect.top); + const iy1 = Math.min(mapRect.height, r.bottom - mapRect.top); + if (ix1 > ix0 && iy1 > iy0) { + const isFullWidth = (ix1 - ix0) >= mapRect.width * 0.8; + const isFullHeight = (iy1 - iy0) >= mapRect.height * 0.5; + if (isFullWidth) { + if (iy0 <= 6) vy0 = iy1; + else if (iy1 >= mapRect.height - 6) vy1 = iy0; + } else if (isFullHeight) { + if (ix0 <= 6) vx0 = ix1; + else if (ix1 >= mapRect.width - 6) vx1 = ix0; + } else { + if (iy1 >= mapRect.height - 6) vy1 = iy0; + } + } + const pt = map.latLngToContainerPoint(latlng); + const padding = 24; + const occluded = + pt.x < vx0 + padding || pt.x > vx1 - padding || + pt.y < vy0 + padding || pt.y > vy1 - padding; + if (!occluded && opts.force !== true) return; + const cx = (vx0 + vx1) / 2; + const cy = (vy0 + vy1) / 2; + const dx = pt.x - cx; + const dy = pt.y - cy; + if (Math.abs(dx) > 2 || Math.abs(dy) > 2) { + map.panBy([dx, dy], { animate: opts.animate !== false }); + } + } catch (_) {} + } + + function setCollapsed(v) { + isCollapsed = !!v; + const root = el(); + if (!root) return; + root.classList.toggle('vinf--collapsed', isCollapsed); + root.setAttribute('aria-expanded', isCollapsed ? 'false' : 'true'); + // When collapsing/expanding the desktop panel, we may shift from being clipped to visible; + // re-run positioning on desktop so it stays anchored correctly. + if (!isMobile() && !userPositioned) { + requestAnimationFrame(() => _positionNearMarker()); + } + } + + function _attachOnce() { + const root = el(); + if (!root || root._bound) return; + root._bound = true; + + root.addEventListener('click', (e) => { + const btn = e.target.closest('[data-act]'); + if (!btn) return; + const act = btn.dataset.act; + const mmsi = openMmsi; + const v = mmsi != null ? getAisTargetByMmsi(mmsi) : null; + if (act === 'close') { close(); return; } + if (act === 'collapse') { setCollapsed(!isCollapsed); return; } + if (act === 'center' && v && v.lat != null && v.lon != null) { + map.setView([v.lat, v.lon], Math.max(map.getZoom(), 14)); + requestAnimationFrame(() => _panMarkerIntoView(L.latLng(v.lat, v.lon))); + return; + } + if (act === 'ruler' && v && v.lat != null && v.lon != null) { + try { RulerTool.startFromOwnshipTo(v.lat, v.lon); } catch (_) {} + return; + } + if (act === 'copy' && v && v.lat != null && v.lon != null) { + const txt = Number(v.lat).toFixed(6) + ', ' + Number(v.lon).toFixed(6); + try { + if (navigator.clipboard && navigator.clipboard.writeText) { + navigator.clipboard.writeText(txt); + } + } catch (_) {} + btn.textContent = 'Скопировано'; + setTimeout(() => { try { refreshIfOpen(mmsi); } catch (_) {} }, 1200); + return; + } + }); + + // Desktop drag + root.addEventListener('pointerdown', (e) => { + if (isMobile()) return; + if (e.target.closest('[data-act]')) return; + const handle = e.target.closest('[data-drag-handle]'); + if (!handle) return; + e.preventDefault(); + const rect = root.getBoundingClientRect(); + _drag = { id: e.pointerId, dx: e.clientX - rect.left, dy: e.clientY - rect.top }; + try { root.setPointerCapture(e.pointerId); } catch (_) {} + userPositioned = true; + }); + root.addEventListener('pointermove', (e) => { + if (!_drag || e.pointerId !== _drag.id) return; + const mapRect = map.getContainer().getBoundingClientRect(); + let x = e.clientX - mapRect.left - _drag.dx; + let y = e.clientY - mapRect.top - _drag.dy; + const w = root.offsetWidth, h = root.offsetHeight, m = 4; + if (x < m) x = m; + if (y < m) y = m; + if (x + w > mapRect.width - m) x = mapRect.width - m - w; + if (y + h > mapRect.height - m) y = mapRect.height - m - h; + root.style.left = Math.round(x) + 'px'; + root.style.top = Math.round(y) + 'px'; + root.style.right = ''; + root.style.bottom = ''; + }); + const endDrag = (e) => { + if (!_drag) return; + if (e && e.pointerId !== _drag.id) return; + _drag = null; + }; + root.addEventListener('pointerup', endDrag); + root.addEventListener('pointercancel', endDrag); + + // Mobile swipe on the header grip: + // - swipe DOWN: collapse (if expanded) or close (if already collapsed) + // - swipe UP : expand (if collapsed) + let sw = null; + root.addEventListener('touchstart', (e) => { + if (!isMobile()) return; + if (!e.target.closest('[data-drag-handle]')) return; + const t = e.touches[0]; + if (!t) return; + sw = { y0: t.clientY, id: t.identifier, fired: false }; + }, { passive: true }); + root.addEventListener('touchmove', (e) => { + if (!sw || sw.fired) return; + const t = Array.from(e.touches).find(tt => tt.identifier === sw.id); + if (!t) return; + const dy = t.clientY - sw.y0; + // Swipe only collapses/expands; the close (X) button is the only way to close. + if (dy > 48 && !isCollapsed) { sw.fired = true; setCollapsed(true); } + else if (dy < -48 && isCollapsed) { sw.fired = true; setCollapsed(false); } + }, { passive: true }); + root.addEventListener('touchend', () => { sw = null; }, { passive: true }); + + // Close on Esc + document.addEventListener('keydown', (e) => { + if (e.key === 'Escape' && openMmsi != null) close(); + }); + + // Follow marker when map pans/zooms (unless user dragged it already) + try { + map.on('zoomend moveend rotate', () => { + if (openMmsi != null && !userPositioned) _positionNearMarker(); + }); + } catch (_) {} + } + + function open(mmsi) { + const root = el(); + if (!root) return; + _attachOnce(); + const prev = openMmsi; + openMmsi = mmsi; + selectedMmsi = String(mmsi); + if (prev !== mmsi) { + userPositioned = false; + isCollapsed = false; + } + + const v = getAisTargetByMmsi(mmsi); + if (!v) return; + root.innerHTML = _buildHtml(v); + root.hidden = false; + root.setAttribute('aria-hidden', 'false'); + root.classList.toggle('vinf--collapsed', isCollapsed); + root.setAttribute('aria-expanded', isCollapsed ? 'false' : 'true'); + + if (!userPositioned) { + requestAnimationFrame(() => _positionNearMarker()); + } + // After layout, pan the map so the marker is not hidden behind the sheet/card. + if (v.lat != null && v.lon != null) { + requestAnimationFrame(() => _panMarkerIntoView(L.latLng(v.lat, v.lon))); + } + try { + const ov = vesselOverlays.get(mmsi); + if (v.lat != null && v.lon != null) { + updateVesselOverlay(mmsi, v.lat, v.lon, true, + !!(ov && ov.losing), + (_markerForTargetMmsi(mmsi) && _markerForTargetMmsi(mmsi)._bearingForOverlay)); + } + } catch (_) {} + try { renderVesselSidebar(); } catch (_) {} + try { renderTargetsTab(); } catch (_) {} + try { if (typeof NearbyHud !== 'undefined') NearbyHud.render(); } catch (_) {} + } + function close() { + const root = el(); + if (!root) return; + const was = openMmsi; + openMmsi = null; + if (String(selectedMmsi) === String(was)) selectedMmsi = null; + root.hidden = true; + root.setAttribute('aria-hidden', 'true'); + if (was != null) { + try { + const v = getAisTargetByMmsi(was); + if (v && v.lat != null && v.lon != null) { + const ov = vesselOverlays.get(was); + updateVesselOverlay(was, v.lat, v.lon, false, + !!(ov && ov.losing), + (_markerForTargetMmsi(was) && _markerForTargetMmsi(was)._bearingForOverlay)); + } + } catch (_) {} + try { renderVesselSidebar(); } catch (_) {} + try { renderTargetsTab(); } catch (_) {} + try { if (typeof NearbyHud !== 'undefined') NearbyHud.render(); } catch (_) {} + } + } + function refreshIfOpen(mmsi) { + if (openMmsi == null || String(openMmsi) !== String(mmsi)) return; + const root = el(); + if (!root || root.hidden) return; + const v = getAisTargetByMmsi(mmsi); + if (!v) return; + root.innerHTML = _buildHtml(v); + root.classList.toggle('vinf--collapsed', isCollapsed); + root.setAttribute('aria-expanded', isCollapsed ? 'false' : 'true'); + } + function currentMmsi() { return openMmsi; } + function panIntoView(lat, lon, opts) { _panMarkerIntoView(L.latLng(lat, lon), opts || {}); } + + return { open, close, refreshIfOpen, currentMmsi, panIntoView, setCollapsed }; +})(); + +// Back-compat shim: existing code checked `openPopups.has(mmsi)` to decide whether +// a vessel is "chosen". Keep the same read-interface but delegate to VesselInfoWindow. +const openPopups = { + has(mmsi) { return String(VesselInfoWindow.currentMmsi()) === String(mmsi); }, + add() {}, delete() {}, clear() {}, +}; + +// ===================== Ruler tool (on-map distance measurement) ===================== +// - Click to set first point, click again to set end point (or move cursor before second click). +// - Supports "from ownship to target" shortcut via startFromOwnshipTo(). +// - HUD in lower-right shows NM / km / bearing and a close button. +const RulerTool = (function () { + let on = false; + let line = null, endMarker = null, startMarker = null; + let startLL = null, endLL = null; + let hud = null; + let onClick = null, onMove = null; + + function _ensureHud() { + if (hud) return hud; + hud = document.createElement('div'); + hud.className = 'ruler-hud'; + hud.hidden = true; + hud.innerHTML = + 'Клик — начальная точка, ещё клик — конечная. Esc — выход.' + + '' + + ''; + document.body.appendChild(hud); + hud.querySelector('button').addEventListener('click', disable); + return hud; + } + function _updateHud() { + _ensureHud(); + hud.hidden = false; + const d = hud.querySelector('.dist'); + if (!startLL) { d.textContent = 'Кликните на карте'; return; } + if (!endLL) { d.textContent = 'Выберите вторую точку…'; return; } + const nm = haversineNM(startLL.lat, startLL.lng, endLL.lat, endLL.lng); + const km = nm * 1.852; + const brg = bearingDeg(startLL.lat, startLL.lng, endLL.lat, endLL.lng); + d.textContent = nm.toFixed(2) + ' NM · ' + km.toFixed(2) + ' км · ' + brg.toFixed(0) + '°'; + } + function _draw() { + if (!startLL) return; + const pts = [startLL, endLL || startLL]; + if (!line) line = L.polyline(pts, { color: '#d2ff1a', weight: 3, dashArray: '6,4', interactive: false }).addTo(map); + else line.setLatLngs(pts); + if (!startMarker) startMarker = L.circleMarker(startLL, { radius: 5, color: '#d2ff1a', fillColor: '#d2ff1a', fillOpacity: 0.9, interactive: false }).addTo(map); + else startMarker.setLatLng(startLL); + if (endLL) { + if (!endMarker) endMarker = L.circleMarker(endLL, { radius: 6, color: '#d2ff1a', fillColor: '#d2ff1a', fillOpacity: 0.6, interactive: false }).addTo(map); + else endMarker.setLatLng(endLL); + } + } + function enable() { + if (on) return; + on = true; + document.body.classList.add('ruler-active'); + try { map.getContainer().style.cursor = 'crosshair'; } catch (_) {} + const btn = document.getElementById('mc-ruler'); + if (btn) btn.classList.add('active'); + startLL = null; endLL = null; + _updateHud(); + onClick = (e) => { + if (!startLL) { startLL = e.latlng; endLL = null; _draw(); _updateHud(); return; } + if (!endLL) { endLL = e.latlng; _draw(); _updateHud(); return; } + // restart measurement + startLL = e.latlng; endLL = null; _draw(); _updateHud(); + }; + onMove = (e) => { + if (startLL && !endLL && line) line.setLatLngs([startLL, e.latlng]); + }; + map.on('click', onClick); + map.on('mousemove', onMove); + } + function disable() { + if (!on) return; + on = false; + document.body.classList.remove('ruler-active'); + try { map.getContainer().style.cursor = ''; } catch (_) {} + if (onClick) map.off('click', onClick); + if (onMove) map.off('mousemove', onMove); + onClick = null; onMove = null; + if (line) { try { map.removeLayer(line); } catch (_) {} line = null; } + if (startMarker) { try { map.removeLayer(startMarker); } catch (_) {} startMarker = null; } + if (endMarker) { try { map.removeLayer(endMarker); } catch (_) {} endMarker = null; } + startLL = null; endLL = null; + if (hud) hud.hidden = true; + const btn = document.getElementById('mc-ruler'); + if (btn) btn.classList.remove('active'); + } + function toggle() { on ? disable() : enable(); } + function startFromOwnshipTo(lat, lon) { + const os = getOwnShipPos(); + enable(); + if (os && os.lat != null && os.lon != null) { + startLL = L.latLng(os.lat, os.lon); + endLL = L.latLng(lat, lon); + _draw(); _updateHud(); + try { map.fitBounds(L.latLngBounds([startLL, endLL]).pad(0.25)); } catch (_) {} + } + } + document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && on) disable(); }); + return { enable, disable, toggle, startFromOwnshipTo, isOn: () => on }; +})(); + +// ===================== Map controls (zoom, centre, north-up, ruler, one-hand) ===================== +(function initMapControls() { + function bind(id, fn) { + const el = document.getElementById(id); + if (!el) return; + el.addEventListener('click', (e) => { e.preventDefault(); fn(e); }); + } + const centerOnOwnship = () => { + const os = getOwnShipPos(); + if (os && os.lat != null && os.lon != null) { + map.setView([os.lat, os.lon], Math.max(map.getZoom(), 13)); + } + }; + bind('mc-zoom-in', () => map.zoomIn()); + bind('mc-zoom-out', () => map.zoomOut()); + bind('mc-center', centerOnOwnship); + bind('mc-north-up', () => { + try { if (typeof map.setBearing === 'function') map.setBearing(0); } catch (_) {} + try { + if (typeof rotateMapByCompass !== 'undefined' && rotateMapByCompass) { + rotateMapByCompass = false; + try { sSet('rotateMapByCompass', false); } catch (_) {} + try { _setOwnshipCompassUi(); } catch (_) {} + try { _reflectCompassToggleUi(); } catch (_) {} + } + } catch (_) {} + }); + bind('mc-compass', () => { + try { _toggleRotateMapByCompass(); } catch (_) {} + }); + try { _reflectCompassToggleUi(); } catch (_) {} + bind('mc-ruler', () => RulerTool.toggle()); + + // One-hand pad buttons + bind('oh-zoom-in', () => map.zoomIn()); + bind('oh-zoom-out', () => map.zoomOut()); + bind('oh-center', centerOnOwnship); + + // One-hand mode toggle (big buttons + single-finger double-tap-drag zoom) + const onehandBtn = document.getElementById('mc-onehand'); + const onehandPad = document.getElementById('onehand-pad'); + function setOnehand(v) { + document.body.classList.toggle('onehand', !!v); + if (onehandPad) { + onehandPad.hidden = !v; + onehandPad.setAttribute('aria-hidden', v ? 'false' : 'true'); + } + if (onehandBtn) onehandBtn.classList.toggle('active', !!v); + try { sSet('onehand', v ? '1' : '0'); } catch (_) {} + } + try { if (sGet('onehand', '0') === '1') setOnehand(true); } catch (_) {} + if (onehandBtn) onehandBtn.addEventListener('click', () => { + setOnehand(!document.body.classList.contains('onehand')); + }); + + // Double-tap-drag zoom on a single finger (Google-Maps-style) — active only in one-hand mode. + try { + const mapEl = map.getContainer(); + let lastTap = null, dragState = null; + mapEl.addEventListener('touchstart', (e) => { + if (!document.body.classList.contains('onehand')) return; + if (e.touches.length !== 1) { lastTap = null; return; } + const t = e.touches[0]; + const now = Date.now(); + if (lastTap && (now - lastTap.ts) < 320 && + Math.hypot(t.clientX - lastTap.x, t.clientY - lastTap.y) < 40) { + const rect = mapEl.getBoundingClientRect(); + const pt = L.point(t.clientX - rect.left, t.clientY - rect.top); + dragState = { y0: t.clientY, zoom0: map.getZoom(), centerLL: map.containerPointToLatLng(pt) }; + try { map.dragging.disable(); } catch (_) {} + e.preventDefault(); + } + lastTap = { x: t.clientX, y: t.clientY, ts: now }; + }, { passive: false }); + mapEl.addEventListener('touchmove', (e) => { + if (!dragState || e.touches.length !== 1) return; + const t = e.touches[0]; + const dy = dragState.y0 - t.clientY; + const z = dragState.zoom0 + dy / 80; + const zMin = map.getMinZoom(), zMax = map.getMaxZoom(); + map.setZoomAround(dragState.centerLL, Math.max(zMin, Math.min(zMax, z))); + e.preventDefault(); + }, { passive: false }); + const endOneFingerZoom = () => { + if (dragState) { dragState = null; try { map.dragging.enable(); } catch (_) {} } + }; + mapEl.addEventListener('touchend', endOneFingerZoom); + mapEl.addEventListener('touchcancel', endOneFingerZoom); + } catch (_) {} +})(); + +// ===================== New "Targets" tab (primary full list on mobile) ===================== +function renderTargetsTab() { + const list = document.getElementById('targets-list'); + const cntEl = document.getElementById('targets-count'); + const suffEl = document.getElementById('targets-count-suffix'); + if (!list) return; + + const combined = [] + .concat(lastAnyVessels || []) + .concat(lastBaseStations || []) + .concat(lastBuoys || []); + const os = getOwnShipPos(); + if (os) { + for (const v of combined) { + if (v && v.lat != null && v.lon != null) v._distNM = haversineNM(os.lat, os.lon, v.lat, v.lon); + else v._distNM = null; + } + } + + const clsSel = document.getElementById('targets-filter-class'); + const stSel = document.getElementById('targets-filter-shiptype'); + const searchEl = document.getElementById('targets-search'); + const sortEl = document.getElementById('targets-sort'); + const cls = clsSel ? clsSel.value : 'all'; + const stG = stSel ? stSel.value : 'all'; + const q = (searchEl && searchEl.value ? searchEl.value : '').trim().toLowerCase(); + const sortMode = sortEl ? sortEl.value : 'time-desc'; + + const filtered = combined.filter(v => { + if (cls !== 'all' && String(v.vessel_class || '') !== cls) return false; + if (typeof matchesShiptypeFilter === 'function' && !matchesShiptypeFilter(v, stG)) return false; + if (q) { + const hay = (typeof vesselSearchHaystack === 'function') ? vesselSearchHaystack(v) : String(v.mmsi || '').toLowerCase(); + if (!hay.includes(q)) return false; + } + return true; + }); + try { if (typeof sortVesselsInPlace === 'function') sortVesselsInPlace(filtered, sortMode); } catch (_) {} + + if (cntEl) cntEl.textContent = String(filtered.length); + if (suffEl) suffEl.textContent = filtered.length !== combined.length ? ' из ' + combined.length : ''; + + list.innerHTML = ''; + for (const v of filtered) { + const { mmsi, lat, lon, vessel_class, shipname, speed, course, heading, timestamp } = v; + const it = document.createElement('div'); + it.className = 'vessel-item'; + it.dataset.mmsi = String(mmsi); + const hasCoord = lat != null && lon != null; + const iso2 = mmsiToIso2FromMid(mmsi); + const flag = iso2ToFlagEmoji(iso2); + const brg = (os && hasCoord) ? bearingDeg(os.lat, os.lon, lat, lon) : null; + + let h = '
' + escHtml(mmsi) + ''; + if (flag) h += '' + flag + ''; + h += '
'; + if (shipname) h += '
' + escHtml(shipname) + '
'; + const clsLabel = (vessel_class === 'BS') ? 'База' : (vessel_class === 'N') ? 'Буёк' : (vessel_class || '?'); + h += '
Класс: ' + escHtml(clsLabel) + '
'; + if (String(vessel_class) === 'N' && v.aton_type != null && v.aton_type !== '') { + h += '
Тип: ' + escHtml(v.aton_type_label || atonTypeLabel(v.aton_type) || 'Тип СНО не указан') + '
'; + } + if (v._distNM != null || brg != null) { + let distLine = '
'; + if (v._distNM != null) distLine += '' + fmtDist(v._distNM) + ''; + if (brg != null) distLine += '' + brg.toFixed(0) + '°'; + distLine += '
'; + h += distLine; + } + h += '
' + + '' + + 'SOG' + (speed != null ? escHtml(fmtSpeed(speed)) : '—') + '' + + '' + + 'COG' + (course != null ? course.toFixed(0) + '°' : '—') + '' + + '' + + 'HDG' + (heading != null ? heading.toFixed(0) + '°' : '—') + '' + + (v.signal_db != null ? '' + + 'SIG' + Number(v.signal_db).toFixed(1) + ' дБ' : '') + + '
'; + if (timestamp != null) h += '
' + fmtTime(timestamp) + ' (' + fmtAgo(timestamp) + ')
'; + it.innerHTML = h; + + if (hasCoord) { + if (String(mmsi) === (selectedMmsi != null ? String(selectedMmsi) : null)) it.classList.add('selected'); + it.addEventListener('click', () => { + selectedMmsi = String(mmsi); + switchTab('map'); + setTimeout(() => { + map.setView([lat, lon], Math.max(map.getZoom(), 15), { animate: false }); + try { VesselInfoWindow.open(mmsi); } catch (_) {} + try { NearbyHud.render(); } catch (_) {} + }, 80); + }); + } + list.appendChild(it); + } +} + +(function initTargetsTabControls() { + ['targets-search', 'targets-sort', 'targets-filter-class', 'targets-filter-shiptype'].forEach(id => { + const el = document.getElementById(id); + if (!el) return; + const ev = (el.tagName === 'INPUT') ? 'input' : 'change'; + el.addEventListener(ev, () => { try { renderTargetsTab(); } catch (_) {} }); + }); +})(); + +// ===================== Mobile compact sidebar toggle ===================== +// On narrow viewports switch the "Ближайшие" sidebar to a compact row layout +// (MMSI / NAME / DIST + SOG/COG/HDG/BRG/DIST line). Desktop keeps verbose layout. +(function initMobileCompactSidebar() { + const sb = document.getElementById('sidebar'); + if (!sb) return; + let mql; + try { mql = window.matchMedia('(max-width:600px)'); } catch (_) { return; } + const apply = () => sb.classList.toggle('sidebar--compact', !!mql.matches); + apply(); + try { mql.addEventListener('change', apply); } catch (_) { + if (mql.addListener) mql.addListener(apply); + } +})(); + +// ===================== Nearby HUD ===================== +// Semi-transparent corner overlay: own-ship readout (speed / coords / compass) +// + the 5 nearest vessels OR the single currently selected vessel (with a clear-X). +// Clicking an item selects the vessel and opens the infowindow. +const NearbyHud = (function(){ + const root = document.getElementById('nearby-hud'); + if (!root) return { render: ()=>{} }; + const listEl = document.getElementById('nhud-list'); + const ownRow = document.getElementById('nhud-own'); + const ownSog = document.getElementById('nhud-own-sog'); + const ownCog = document.getElementById('nhud-own-cog'); + const ownCoords = document.getElementById('nhud-own-coords'); + const ownSrc = document.getElementById('nhud-own-src'); + const compassEl = document.getElementById('nhud-compass'); + const needle = document.getElementById('nhud-needle'); + const compassVal = document.getElementById('nhud-compass-val'); + const toggleBtn = document.getElementById('nhud-toggle'); + + let collapsed = false; + try { collapsed = sGet('nhudCollapsed', '0') === '1'; } catch(_) {} + root.classList.toggle('is-collapsed', collapsed); + + if (toggleBtn) { + toggleBtn.addEventListener('click', () => { + collapsed = !collapsed; + root.classList.toggle('is-collapsed', collapsed); + try { sSet('nhudCollapsed', collapsed ? '1' : '0'); } catch(_) {} + }); + } + + function fmtCoords(lat, lon) { + if (lat == null || lon == null) return '—'; + return lat.toFixed(4) + ', ' + lon.toFixed(4); + } + function fmtBrg(b) { return (b != null && !isNaN(b)) ? b.toFixed(0) + '°' : '—'; } + + function buildItemHtml(v, isSelected) { + const { mmsi, shipname, callsign, lat, lon, speed, course } = v; + const name = (shipname && String(shipname).trim()) || ''; + const cs = (callsign && String(callsign).trim()) || ''; + const brg = (v._brg != null) ? v._brg : null; + const distTxt = (v._distNM != null) ? fmtDist(v._distNM) : '—'; + + let idHtml = ''; + if (name) idHtml += '' + escHtml(name) + ''; + if (cs) idHtml += '' + escHtml(cs) + ''; + idHtml += '' + escHtml(mmsi) + ''; + + let botHtml = 'SOG' + + (speed != null ? escHtml(fmtSpeed(speed)) : '—') + ''; + botHtml += 'COG' + + (course != null ? course.toFixed(0) + '°' : '—') + ''; + botHtml += '' + fmtBrg(brg) + ''; + + return '
' + + '' + idHtml + '' + + '' + escHtml(distTxt) + '' + + '
' + + '
' + botHtml + '
' + + '
' + escHtml(fmtCoords(lat, lon)) + '
' + + (isSelected ? '' : ''); + } + + function renderOwn() { + const data = (typeof ownShipSource !== 'undefined' && ownShipSource === 'phone') ? phoneGps : nmeaGps; + const hasFix = data && data.lat != null && data.lon != null; + if (hasFix) { + ownCoords.textContent = fmtCoords(data.lat, data.lon); + ownSog.textContent = data.speed != null ? fmtSpeed(data.speed) : '—'; + ownCog.textContent = data.course != null ? data.course.toFixed(0) + '°' : '—'; + } else { + ownCoords.textContent = '—'; + ownSog.textContent = '—'; + ownCog.textContent = '—'; + } + // source label + let srcTxt = ''; + let srcErr = false; + if (ownShipSource === 'phone') { + if (phoneGpsError) { srcTxt = phoneGpsError; srcErr = true; } + else if (hasFix) srcTxt = 'PHONE GPS'; + else srcTxt = 'PHONE GPS (ожидание…)'; + } else { + srcTxt = hasFix ? 'NMEA' : 'NMEA (нет данных)'; + } + ownSrc.textContent = srcTxt; + ownSrc.classList.toggle('nhud-own__src--err', srcErr); + + // Compass: prefer phone compass when available; else GPS heading/course; else nothing + const compassH = (typeof phoneCompassOk !== 'undefined' && phoneCompassOk && phoneCompassHeading != null) + ? phoneCompassHeading : null; + const h = (compassH != null) + ? compassH + : (hasFix ? (data.heading != null ? data.heading : data.course) : null); + const hasHeading = h != null && !isNaN(h); + compassEl.classList.toggle('no-data', !hasHeading); + compassEl.classList.toggle('from-phone', compassH != null); + compassEl.classList.toggle('has-val', hasHeading); + if (hasHeading) { + needle.style.transform = 'rotate(' + (-h) + 'deg)'; + compassVal.textContent = h.toFixed(0) + '°'; + } else { + compassVal.textContent = ''; + } + } + + function renderList() { + const os = getOwnShipPos(); + const combined = [] + .concat(lastAnyVessels || []) + .concat(lastBaseStations || []) + .concat(lastBuoys || []); + + // Compute distance+bearing from own ship + if (os) { + for (const v of combined) { + if (v && v.lat != null && v.lon != null) { + v._distNM = haversineNM(os.lat, os.lon, v.lat, v.lon); + v._brg = bearingDeg(os.lat, os.lon, v.lat, v.lon); + } else { + v._distNM = null; v._brg = null; + } + } + } + + const hasSel = selectedMmsi != null; + let items = []; + if (hasSel) { + const sel = combined.find(v => String(v.mmsi) === String(selectedMmsi)); + if (sel) items.push({ v: sel, isSel: true }); + } + if (!items.length || !hasSel) { + // Top-5 by distance (if own position known); else by time desc + let pool = combined.filter(v => v && v.lat != null && v.lon != null); + if (hasSel) pool = pool.filter(v => String(v.mmsi) !== String(selectedMmsi)); + if (os) { + pool.sort((a,b) => (a._distNM != null ? a._distNM : Infinity) - (b._distNM != null ? b._distNM : Infinity)); + } else { + pool.sort((a,b) => (b.timestamp||0) - (a.timestamp||0)); + } + const need = hasSel ? 4 : 5; + for (const v of pool.slice(0, need)) items.push({ v, isSel: false }); + } + + if (!items.length) { + listEl.innerHTML = '
Нет целей в эфире
'; + root.classList.add('is-empty'); + return; + } + root.classList.remove('is-empty'); + + const html = items.map(({ v, isSel }) => { + return '
' + + buildItemHtml(v, isSel) + '
'; + }).join(''); + listEl.innerHTML = html; + + // Wire up click handlers + listEl.querySelectorAll('.nhud-item').forEach(el => { + const mmsi = el.dataset.mmsi; + el.addEventListener('click', (ev) => { + if (ev.target.closest('.nhud-item__clear')) { + selectedMmsi = null; + try { VesselInfoWindow.close(); } catch(_) {} + try { renderVesselSidebar(); } catch(_) {} + render(); + return; + } + selectedMmsi = String(mmsi); + const v = combined.find(x => String(x.mmsi) === String(mmsi)); + if (v && v.lat != null && v.lon != null) { + try { map.setView([v.lat, v.lon], Math.max(map.getZoom(), 14), { animate: true }); } catch(_) {} + try { VesselInfoWindow.open(mmsi); } catch(_) {} + } + render(); + }); + }); + } + + let _renderPending = false; + function render() { + if (_renderPending) return; + _renderPending = true; + requestAnimationFrame(() => { + _renderPending = false; + try { renderOwn(); } catch(_) {} + try { renderList(); } catch(_) {} + }); + } + + // Short ticker for compass/coords (updates even between vessel feeds). + setInterval(() => { try { renderOwn(); } catch(_) {} }, 500); + // Full re-render pace + setInterval(() => { try { render(); } catch(_) {} }, 2000); + + return { render }; +})(); + +/** Stern (icon anchor) → midpoint of hull along keel; bearing ° from north like AIS heading. */ +function overlayLatLngFromStern(lat, lon, bearingDeg) { + if (bearingDeg == null || isNaN(bearingDeg)) return [lat, lon]; + const ll = L.latLng(lat, lon); + const p = map.latLngToContainerPoint(ll); + if (!p) return [lat, lon]; + const mpp = map.distance(ll, map.containerPointToLatLng(L.point(p.x, p.y + 1))); + const distM = (iconHeight / 2) * mpp; + const R = 6371000; + const brng = bearingDeg * Math.PI / 180; + const φ1 = lat * Math.PI / 180, λ1 = lon * Math.PI / 180; + const δ = distM / R; + const sinφ1 = Math.sin(φ1), cosφ1 = Math.cos(φ1); + const sinδ = Math.sin(δ), cosδ = Math.cos(δ); + const sinφ2 = sinφ1 * cosδ + cosφ1 * sinδ * Math.cos(brng); + const φ2 = Math.asin(sinφ2); + const λ2 = λ1 + Math.atan2(Math.sin(brng) * sinδ * cosφ1, cosδ - sinφ1 * sinφ2); + return [φ2 * 180 / Math.PI, λ2 * 180 / Math.PI]; +} + +function updateVesselOverlay(mmsi, lat, lon, isChosen, isLosing, bearingDeg) { + const [olat, olng] = overlayLatLngFromStern(lat, lon, bearingDeg); + if (!vesselOverlays.has(mmsi)) vesselOverlays.set(mmsi, {chosen:null, losing:null}); + const o = vesselOverlays.get(mmsi); + if (isChosen && !o.chosen) o.chosen = L.marker([olat,olng],{icon:iconChosen,zIndexOffset:1000}).addTo(map); + else if (!isChosen && o.chosen) { map.removeLayer(o.chosen); o.chosen=null; } + else if (isChosen && o.chosen) o.chosen.setLatLng([olat,olng]); + if (isLosing && !o.losing) o.losing = L.marker([olat,olng],{icon:iconLosing,zIndexOffset:1000}).addTo(map); + else if (!isLosing && o.losing) { map.removeLayer(o.losing); o.losing=null; } + else if (isLosing && o.losing) o.losing.setLatLng([olat,olng]); +} +function removeVesselOverlay(mmsi) { + if (!vesselOverlays.has(mmsi)) return; + const o = vesselOverlays.get(mmsi); + if (o.chosen) map.removeLayer(o.chosen); + if (o.losing) map.removeLayer(o.losing); + vesselOverlays.delete(mmsi); +} + +function fmtAgo(ts) { + if (!ts) return '?'; + const d = Math.floor(Date.now()/1000) - ts; + if (d<0) return '?'; + if (d<60) return d+'с'; + const m=Math.floor(d/60), s=d%60; + if (m<60) return m+'м '+s+'с'; + return Math.floor(m/60)+'ч '+m%60+'м'; +} +function fmtTime(ts) { + if (!ts) return '?'; + const d=new Date(ts*1000); + return [d.getHours(),d.getMinutes(),d.getSeconds()].map(v=>String(v).padStart(2,'0')).join(':'); +} + +function _clientLog(level, msg, ctx) { + try { + try { console.log('[AISMap] clientLog', level, msg, ctx || null); } catch (e) {} + // Best-effort: do not block UI; keepalive helps on page unload. + const payload = { + level: level || 'info', + msg: msg || '', + ctx: ctx || null, + ts: Math.floor(Date.now() / 1000), + url: (typeof location !== 'undefined' ? location.href : null), + ua: (typeof navigator !== 'undefined' ? navigator.userAgent : null), + build: (typeof APP_BUILD !== 'undefined' ? APP_BUILD : null), + }; + // Prefer Beacon (more reliable on mobile/WebView); fall back to fetch. + try { + if (navigator && typeof navigator.sendBeacon === 'function') { + const blob = new Blob([JSON.stringify(payload)], { type: 'application/json' }); + navigator.sendBeacon('/api/client_log', blob); + return; + } + } catch (e) {} + fetch('/api/client_log', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(payload), + keepalive: true, + }).catch(() => {}); + } catch (e) {} +} + +function _numOrNull(x) { + if (x == null) return null; + if (typeof x === 'number') return Number.isFinite(x) ? x : null; + const n = parseFloat(x); + return Number.isFinite(n) ? n : null; +} + +function _isMockApiEnabled() { + try { + const q = new URLSearchParams(location.search || ''); + if (q.get('mock') === '1') return true; + } catch (e) {} + try { return sGet('mockApi', '0') === '1'; } catch (e) { return false; } +} + +function _apiUrl(path) { + if (!_isMockApiEnabled()) return path; + try { + const u = new URL(path, location.origin); + u.searchParams.set('mock', '1'); + return u.pathname + '?' + u.searchParams.toString(); + } catch (e) { + return path + (path.includes('?') ? '&' : '?') + 'mock=1'; + } +} + +function _extractServerNowAndList(payload, key) { + // Backward-compatible: old APIs returned plain arrays. + if (Array.isArray(payload)) { + return { serverNow: Math.floor(Date.now() / 1000), list: payload }; + } + if (payload && typeof payload === 'object') { + const sn = payload.server_now != null ? parseInt(payload.server_now, 10) : null; + const list = payload[key] != null ? payload[key] : (payload.data != null ? payload.data : []); + return { + serverNow: (sn != null && !isNaN(sn)) ? sn : Math.floor(Date.now() / 1000), + list: Array.isArray(list) ? list : [] + }; + } + return { serverNow: Math.floor(Date.now() / 1000), list: [] }; +} + +// Debug HUD (left-bottom corner) — disabled because it collides with the +// #nearby-hud overlay. The helpers are kept as no-ops so existing call sites +// keep working and can easily be re-enabled by flipping `_DEBUG_HUD_ENABLED` +// or by removing this guard. +const _DEBUG_HUD_ENABLED = false; + +function _ensureDebugHudOnce() { + if (!_DEBUG_HUD_ENABLED) return; + try { + if (document.getElementById('aismap-debug-hud')) return; + const d = document.createElement('div'); + d.id = 'aismap-debug-hud'; + d.style.cssText = 'position:fixed;left:8px;top:8px;z-index:9999;max-width:92vw;' + + 'background:rgba(0,0,0,.75);color:#e6edf3;font:12px/1.35 system-ui,Segoe UI,Roboto,Arial;' + + 'padding:6px 8px;border-radius:8px;box-shadow:0 4px 16px rgba(0,0,0,.25);' + + 'white-space:pre-wrap;pointer-events:none'; + d.textContent = 'AISMap debug HUD\nbuild: ' + APP_BUILD; + document.body.appendChild(d); + } catch (e) {} +} + +function _setDebugHud(text) { + if (!_DEBUG_HUD_ENABLED) return; + try { + _ensureDebugHudOnce(); + const d = document.getElementById('aismap-debug-hud'); + if (d) d.textContent = text; + } catch (e) {} +} + +function updateVessels() { + try { + // Источник — in-memory AisHub.vessels, наполняется WebSocket-ом /ws. + const vessels = Array.from(AisHub.vessels.values()).map(v => Object.assign({}, v)); + const now = Math.floor(Date.now() / 1000); + _setDebugHud('AISMap debug HUD\nbuild: ' + APP_BUILD + '\nvessels: ' + vessels.length); + const os = getOwnShipPos(); + const maxNM = getRangeNM(); + + // mergedTargetToVessel уже нормализует числа, но на случай кривых событий — страхуем. + for (const v of vessels) { + if (!v) continue; + v.lat = _numOrNull(v.lat); + v.lon = _numOrNull(v.lon); + v.course = _numOrNull(v.course); + v.speed = _numOrNull(v.speed); + v.heading = _numOrNull(v.heading); + v.timestamp = v.timestamp != null ? parseInt(v.timestamp, 10) : v.timestamp; + v.mmsi = v.mmsi != null ? parseInt(v.mmsi, 10) : v.mmsi; + } + + for (const v of vessels) { + if (os && v.lat != null && v.lon != null) + v._distNM = haversineNM(os.lat, os.lon, v.lat, v.lon); + else + v._distNM = null; + } + + if (os) vessels.sort((a, b) => (a._distNM ?? Infinity) - (b._distNM ?? Infinity)); + + const visibleSet = new Set(); + for (const v of vessels) { + const validCoord = v.lat != null && v.lon != null && !isNaN(v.lat) && !isNaN(v.lon) && v.lat >= -90 && v.lat <= 90 && v.lon >= -180 && v.lon <= 180; + const inRange = !isFinite(maxNM) || v._distNM == null || v._distNM <= maxNM; + const notExpired = !_isTargetExpiredByTimestamp(v, now); + if (validCoord && inRange && notExpired) visibleSet.add(v.mmsi); + } + + // Total targets that sent anything (not expired), including those without a "legal" position. + const totalAnySet = new Set(); + for (const v of vessels) { + const notExpired = !_isTargetExpiredByTimestamp(v, now); + if (notExpired) totalAnySet.add(v.mmsi); + } + + // Update sidebar data early (even if marker drawing fails later). + lastAnyVessels = vessels + .filter(v => !_isTargetExpiredByTimestamp(v, now)) + .map(v => Object.assign({}, v, { kind: 'vessel' })); + + try { + console.log('[AISMap] vessels sets', { + visible: visibleSet.size, + totalAny: totalAnySet.size, + sampleVisible: (() => { + for (const v of vessels) { + if (visibleSet.has(v && v.mmsi)) return { mmsi: v.mmsi, lat: v.lat, lon: v.lon, ts: v.timestamp }; + } + return null; + })() + }); + } catch (e) {} + + for (const [mmsi, mk] of vesselMarkers.entries()) { + if (!visibleSet.has(mmsi)) { + if (String(mmsi) === String(selectedMmsi)) selectedMmsi = null; + map.removeLayer(mk); + vesselMarkers.delete(mmsi); + removeVesselOverlay(mmsi); + removeMotionOverlays(mmsi); + removeVesselDetailOverlays(mmsi); + vesselLastData.delete(mmsi); + openPopups.delete(mmsi); + try { if (VesselInfoWindow.currentMmsi() && String(VesselInfoWindow.currentMmsi()) === String(mmsi)) VesselInfoWindow.close(); } catch (_) {} + } + } + + document.getElementById('status').textContent = 'Целей: ' + visibleSet.size + ' (' + totalAnySet.size + ')'; + _setDebugHud('AISMap debug HUD\nbuild: ' + APP_BUILD + + '\n/api/vessels: ' + (Array.isArray(vessels) ? vessels.length : '?') + + '\nvisibleSet: ' + visibleSet.size + ' totalAny: ' + totalAnySet.size); + + _dangerState = { any: false, count: 0, minNm: null, relDegSigned: null }; + + for (const v of vessels) { + try { + if (!v || !visibleSet.has(v.mmsi)) continue; + const {mmsi, lat, lon, vessel_class, shipname, callsign, course, speed, heading, timestamp, shiptype, nav_status} = v; + const moving = speed != null && !isNaN(speed) && speed >= 1.5; + const bearingForOverlay = moving ? (course != null ? course : heading) : (heading != null ? heading : course); + const iconKey = vesselIconTintKey(vessel_class, shiptype, nav_status); + const icon = getVesselDivIcon(vessel_class, shiptype, nav_status); + + if (vesselMarkers.has(mmsi)) { + const mk = vesselMarkers.get(mmsi); + _ensureAisMarkerPane(mk); + mk._bearingForOverlay = bearingForOverlay; + const cl = mk.getLatLng(); + if (Math.abs(cl.lat - lat) > 0.0001 || Math.abs(cl.lng - lon) > 0.0001) mk.setLatLng([lat, lon]); + const ci = mk.options.icon; + if (!ci || mk._vesselIconKey !== iconKey) { + mk.setIcon(icon); + mk._vesselIconKey = iconKey; + requestAnimationFrame(() => setIconRotation(mk, bearingForOverlay)); + } else setIconRotation(mk, bearingForOverlay); + } else { + const mk = L.marker([lat, lon], { icon, pane: AIS_MARKER_PANE || undefined }).addTo(map); + mk.on('click', () => { try { VesselInfoWindow.open(mmsi); } catch (_) {} }); + mk._bearingForOverlay = bearingForOverlay; + mk._vesselIconKey = iconKey; + requestAnimationFrame(() => setIconRotation(mk, bearingForOverlay)); + vesselMarkers.set(mmsi, mk); + } + const age = timestamp ? (now - timestamp) : Infinity; + const isChosen = openPopups.has(mmsi) || String(mmsi) === String(selectedMmsi); + updateVesselOverlay(mmsi, lat, lon, isChosen, age >= LOSING_TARGET_TIME && age < REMOVE_TARGET_TIME, bearingForOverlay); + + // Motion overlays: + // - Vector: ahead by SOG/COG for 1 minute; if ROT is available, bend the vector. + // - Trail: dashed path behind using recent positions. + let refLat = lat, refLon = lon; + const dims = _dimsFromVessel(v); + const brg = _bearingForHull(v); + const refMode = (dims && brg != null) ? 'center' : 'antenna'; + if (vesselTrailRefMode.get(mmsi) !== refMode) { + vesselTrailRefMode.set(mmsi, refMode); + vesselHistory.delete(mmsi); // reset when switching reference point + } + if (refMode === 'center') { + const c = _centerFromAntenna(lat, lon, brg, dims); + refLat = c.lat; + refLon = c.lon; + } + const speedForVector = (speed != null && !isNaN(speed) && speed >= 0 && speed <= 60) ? speed : null; + const courseForVector = (course != null && !isNaN(course)) ? course : null; + updateVesselVector(mmsi, refLat, refLon, courseForVector, speedForVector, v.rot); + updateVesselTrail(mmsi, refLat, refLon, timestamp); + + // Cache for zoom-driven redraw, and update detailed overlays (true-size hull + antenna). + vesselLastData.set(mmsi, v); + updateVesselDetailOverlays(v); + try { VesselInfoWindow.refreshIfOpen(mmsi); } catch (_) {} + + // Proximity logic: warning banner + highlight nearby targets + if (v._distNM != null && isFinite(v._distNM)) { + if (warnRadiusNm > 0 && v._distNM <= warnRadiusNm) { + _dangerState.any = true; + _dangerState.count += 1; + if (_dangerState.minNm == null || v._distNM < _dangerState.minNm) { + _dangerState.minNm = v._distNM; + // Relative bearing to the closest target (for the banner) + const os = getOwnShipPos(); + const osHd = os ? (os.heading != null ? os.heading : os.course) : null; + if (os && os.lat != null && os.lon != null && osHd != null) { + const abs = bearingDeg(os.lat, os.lon, v.lat, v.lon); + _dangerState.relDegSigned = relBearingSignedDeg(osHd, abs); + } else { + _dangerState.relDegSigned = null; + } + } + } + const mk = vesselMarkers.get(mmsi); + if (mk && mk._icon) { + const near = nearRadiusNm > 0 && v._distNM <= nearRadiusNm; + mk._icon.classList.toggle('vessel-nearby', !!near); + } + } + } catch (e) { + console.error('updateVessels vessel:', e, v); + _clientLog('error', 'updateVessels per-vessel failed', { + err: String(e && (e.stack || e.message || e)), + vessel: v || null, + }); + } + } + lastVisibleVessels = vessels.filter(v => visibleSet.has(v.mmsi)).map(v => Object.assign({}, v, { kind: 'vessel' })); + adjustSidebarHeight(); + updateRangeCircle(); + updateDangerCircles(); + updateDangerBanner(); + if (vessels.length > 0 && vesselMarkers.size > 0) { + const f = vessels[0]; + if (f.lat && f.lon && map.getZoom() === 10 && Math.abs(map.getCenter().lat - 55.751244) < 0.001) + map.setView([f.lat, f.lon], 12); + } + } catch (e) { + console.error('updateVessels:', e); + _clientLog('error', 'updateVessels failed', { err: String(e && (e.stack || e.message || e)) }); + _setDebugHud('AISMap debug HUD\nbuild: ' + APP_BUILD + '\nupdateVessels ERROR:\n' + String(e && (e.message || e))); + } +} + +map.on('zoomend', () => { + // Re-evaluate which vessels should show true-size hull at this zoom. + for (const [mmsi, v] of vesselLastData.entries()) { + if (!vesselMarkers.has(mmsi)) continue; + updateVesselDetailOverlays(v); + } +}); + +// ===================== Список целей: MID→флаг, поиск, фильтры, сортировка ===================== +const mmsiMidToIso2 = {}; +let lastSidebarVessels = []; + +function escHtml(s) { + if (s == null || s === '') return ''; + return String(s) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); +} + +function mmsiToIso2FromMid(mmsi) { + const d = String(mmsi).replace(/\D/g, ''); + if (d.length < 3) return null; + const mid = d.slice(0, 3); + return mmsiMidToIso2[mid] || null; +} + +function iso2ToFlagEmoji(iso2) { + if (!iso2 || iso2.length !== 2) return ''; + const a = iso2.toUpperCase(); + if (a.length !== 2 || a < 'AA' || a > 'ZZ') return ''; + const A = 0x1F1E6; + return String.fromCodePoint(A + a.charCodeAt(0) - 65, A + a.charCodeAt(1) - 65); +} + +function vesselSearchHaystack(v) { + const parts = [ + String(v.mmsi != null ? v.mmsi : ''), + (v.shipname && String(v.shipname).trim()) || '', + (v.callsign && String(v.callsign).trim()) || '', + (v.aton_type_label && String(v.aton_type_label).trim()) || '', + (v.aton_type != null ? String(atonTypeLabel(v.aton_type) || '') : ''), + ]; + return parts.join('\u0000').toLowerCase(); +} + +function matchesShiptypeFilter(v, group) { + if (!group || group === 'all') return true; + if (v && v.kind && v.kind !== 'vessel') return false; + const st = v.shiptype; + const c = parseInt(st, 10); + if (group === 'unknown') return st == null || st === '' || c === 0 || isNaN(c); + if (isNaN(c)) return group === 'unknown'; + if (group === 'fishing') return c === 30; + if (group === 'tug') return c === 31 || c === 32; + if (group === 'passenger') return c >= 60 && c <= 69; + if (group === 'cargo') return c >= 70 && c <= 79; + if (group === 'tanker') return c >= 80 && c <= 89; + if (group === 'other') { + if (c === 0) return false; + if (c >= 60 && c <= 89) return false; + if (c === 30 || c === 31 || c === 32) return false; + return true; + } + return true; +} + +function sortVesselsInPlace(list, mode) { + const nameKey = v => ((v.shipname && String(v.shipname).trim()) || '\uFFFF').toLowerCase(); + switch (mode) { + case 'dist-desc': + list.sort((a, b) => { + const da = a._distNM != null && isFinite(a._distNM) ? a._distNM : -1; + const db = b._distNM != null && isFinite(b._distNM) ? b._distNM : -1; + return db - da; + }); + break; + case 'dist-asc': + list.sort((a, b) => { + const da = a._distNM != null && isFinite(a._distNM) ? a._distNM : 1e12; + const db = b._distNM != null && isFinite(b._distNM) ? b._distNM : 1e12; + return da - db; + }); + break; + case 'time-asc': + list.sort((a, b) => (a.timestamp ?? 0) - (b.timestamp ?? 0)); + break; + case 'name-asc': + list.sort((a, b) => nameKey(a).localeCompare(nameKey(b), 'ru', { sensitivity: 'base' })); + break; + case 'mmsi-asc': + list.sort((a, b) => String(a.mmsi).localeCompare(String(b.mmsi), undefined, { numeric: true })); + break; + case 'class-asc': + list.sort((a, b) => String(a.vessel_class || '').localeCompare(String(b.vessel_class || '')) || + String(a.mmsi).localeCompare(String(b.mmsi), undefined, { numeric: true })); + break; + case 'speed-desc': + list.sort((a, b) => { + const sa = a.speed != null && !isNaN(a.speed) ? a.speed : -1; + const sb = b.speed != null && !isNaN(b.speed) ? b.speed : -1; + return sb - sa; + }); + break; + case 'time-desc': + default: + list.sort((a, b) => (b.timestamp ?? 0) - (a.timestamp ?? 0)); + break; + } +} + +function renderVesselSidebar() { + const vl = document.getElementById('vessel-list'); + const cntEl = document.getElementById('vessel-count'); + const suffEl = document.getElementById('vessel-count-suffix'); + if (!vl || !cntEl) return; + + const total = lastSidebarVessels.length; + const maxNM = getRangeNM(); + const clsSel = document.getElementById('vessel-filter-class'); + const stSel = document.getElementById('vessel-filter-shiptype'); + const searchEl = document.getElementById('vessel-search'); + const sortEl = document.getElementById('vessel-sort'); + + const cls = clsSel ? clsSel.value : 'all'; + const stG = stSel ? stSel.value : 'all'; + const q = (searchEl && searchEl.value ? searchEl.value : '').trim().toLowerCase(); + const sortMode = sortEl ? sortEl.value : 'time-desc'; + + const list = lastSidebarVessels.filter(v => { + // Range filter: if we have a computed distance, enforce it; if we don't (no ownship fix), + // keep the item visible to avoid "empty list" confusion. + if (isFinite(maxNM) && v && v._distNM != null && isFinite(v._distNM) && v._distNM > maxNM) return false; + if (cls !== 'all' && String(v.vessel_class || '') !== cls) return false; + if (!matchesShiptypeFilter(v, stG)) return false; + if (q) { + const hay = vesselSearchHaystack(v); + if (!hay.includes(q)) return false; + } + return true; + }); + + sortVesselsInPlace(list, sortMode); + + cntEl.textContent = String(list.length); + if (suffEl) suffEl.textContent = list.length !== total ? ' из ' + total : ''; + + vl.innerHTML = ''; + for (const v of list) { + const { mmsi, lat, lon, vessel_class, shipname, callsign, speed, course, heading, timestamp } = v; + const mmsiKey = String(mmsi); + const it = document.createElement('div'); + it.className = 'vessel-item'; + it.dataset.mmsi = mmsiKey; + + const hasCoord = lat != null && lon != null && !isNaN(lat) && !isNaN(lon) && lat >= -90 && lat <= 90 && lon >= -180 && lon <= 180; + if (!hasCoord) it.classList.add('vessel-item--no-pos'); + + const iso2 = mmsiToIso2FromMid(mmsi); + const flag = iso2ToFlagEmoji(iso2); + const flagTitle = iso2 || ''; + + let h = '
' + escHtml(mmsi) + ''; + if (flag) h += '' + flag + ''; + h += '
'; + if (shipname) h += '
' + escHtml(shipname) + '
'; + if (callsign && String(callsign).trim()) h += '
Позывной: ' + escHtml(String(callsign).trim()) + '
'; + const clsLabel = (vessel_class === 'BS') ? 'База' : (vessel_class === 'N') ? 'Буёк' : (vessel_class || '?'); + h += '
Класс: ' + escHtml(clsLabel) + '
'; + if (String(vessel_class) === 'N' && v.aton_type != null && v.aton_type !== '') { + h += '
Тип: ' + escHtml(v.aton_type_label || atonTypeLabel(v.aton_type) || 'Тип СНО не указан') + '
'; + } + // Ship dimensions (AIS static): show only when we have both length and beam. + const toBow = (typeof v.to_bow === 'number' && isFinite(v.to_bow)) ? v.to_bow : parseInt(v.to_bow, 10); + const toStern = (typeof v.to_stern === 'number' && isFinite(v.to_stern)) ? v.to_stern : parseInt(v.to_stern, 10); + const toPort = (typeof v.to_port === 'number' && isFinite(v.to_port)) ? v.to_port : parseInt(v.to_port, 10); + const toStar = (typeof v.to_starboard === 'number' && isFinite(v.to_starboard)) ? v.to_starboard : parseInt(v.to_starboard, 10); + const lenM = (isFinite(toBow) ? Math.max(0, toBow) : 0) + (isFinite(toStern) ? Math.max(0, toStern) : 0); + const beamM = (isFinite(toPort) ? Math.max(0, toPort) : 0) + (isFinite(toStar) ? Math.max(0, toStar) : 0); + if (lenM > 0 && beamM > 0) h += '
Размер: ' + lenM + '×' + beamM + ' м
'; + const _osP = getOwnShipPos(); + const _brg = (_osP && hasCoord) ? bearingDeg(_osP.lat, _osP.lon, lat, lon) : null; + if (v._distNM != null || _brg != null) { + let distLine = '
'; + if (v._distNM != null) distLine += '' + fmtDist(v._distNM) + ''; + if (_brg != null) distLine += '' + _brg.toFixed(0) + '°'; + distLine += '
'; + h += distLine; + } + if (hasCoord) h += '
' + lat.toFixed(6) + ', ' + lon.toFixed(6) + '
'; + else h += '
Нет координат
'; + if (timestamp != null) h += '
' + fmtTime(timestamp) + ' (' + fmtAgo(timestamp) + ')
'; + // Unified SOG/COG/HDG stats row (styled as inline row on desktop, compact grid on mobile). + // Replaces the separate "Скорость:"/"Курс:" lines that used to appear below (duplicated the stats row). + h += '
' + + '' + + 'SOG' + (speed != null ? escHtml(fmtSpeed(speed)) : '—') + '' + + '' + + 'COG' + (course != null ? course.toFixed(0) + '°' : '—') + '' + + '' + + 'HDG' + (heading != null ? heading.toFixed(0) + '°' : '—') + '' + + '
'; + it.innerHTML = h; + if (hasCoord) { + if (mmsiKey === (selectedMmsi != null ? String(selectedMmsi) : null)) it.classList.add('selected'); + it.addEventListener('click', () => { + selectedMmsi = mmsiKey; + document.querySelectorAll('.vessel-item').forEach(e => e.classList.remove('selected')); + it.classList.add('selected'); + map.setView([lat, lon], Math.max(map.getZoom(), 15), { animate: false }); + try { VesselInfoWindow.open(mmsi); } catch (_) {} + }); + } + vl.appendChild(it); + } +} + +function updateSidebar(vessels) { + lastSidebarVessels = vessels; + renderVesselSidebar(); + try { NearbyHud.render(); } catch (_) {} +} + +function initVesselListControls() { + const search = document.getElementById('vessel-search'); + const sortEl = document.getElementById('vessel-sort'); + const cls = document.getElementById('vessel-filter-class'); + const st = document.getElementById('vessel-filter-shiptype'); + if (!search || !sortEl || !cls || !st) return; + + const SK = 'vesselList'; + const load = (k, def) => sGet(SK + '_' + k, def); + sortEl.value = load('sort', 'time-desc'); + cls.value = load('fclass', 'all'); + st.value = load('ftype', 'all'); + search.value = load('search', ''); + const persist = () => { + sSet(SK + '_sort', sortEl.value); + sSet(SK + '_fclass', cls.value); + sSet(SK + '_ftype', st.value); + sSet(SK + '_search', search.value); + renderVesselSidebar(); + }; + search.addEventListener('input', () => { + sSet(SK + '_search', search.value); + renderVesselSidebar(); + }); + sortEl.addEventListener('change', persist); + cls.addEventListener('change', persist); + st.addEventListener('change', persist); +} + +initVesselListControls(); +fetch('/static/js/mmsi_mid_iso2.json') + .then(r => (r.ok ? r.json() : {})) + .then(o => { Object.assign(mmsiMidToIso2, o); renderVesselSidebar(); }) + .catch(() => {}); + +const cursorCoords=document.getElementById('cursor-coords'); +function fmtZoomZ(){ + try { return map && typeof map.getZoom === 'function' ? String(map.getZoom()) : '?'; } catch(e){ return '?'; } +} +// Throttle cursor coordinate updates to 1/frame (mousemove can be very high frequency). +let _ccPendingLatLng = null; +let _ccRaf = 0; +let _ccLastText = ''; +function _setCursorCoordsText(latlngOrNull){ + if (!cursorCoords) return; + const z = fmtZoomZ(); + const txt = latlngOrNull + ? ('Координаты: ' + latlngOrNull.lat.toFixed(6) + ', ' + latlngOrNull.lng.toFixed(6) + ' | Z: ' + z) + : ('Координаты: - | Z: ' + z); + if (txt !== _ccLastText) { + cursorCoords.textContent = txt; + _ccLastText = txt; + } +} +function _scheduleCursorCoordsFlush(){ + if (_ccRaf) return; + _ccRaf = requestAnimationFrame(() => { + _ccRaf = 0; + _setCursorCoordsText(_ccPendingLatLng); + }); +} +map.on('mousemove', (e) => { + _ccPendingLatLng = e && e.latlng ? e.latlng : null; + _scheduleCursorCoordsFlush(); +}); +map.on('mouseout', () => { + _ccPendingLatLng = null; + _scheduleCursorCoordsFlush(); +}); +map.on('zoomend', () => { + // Keep current latlng text (if any) and refresh Z; do it in rAF too. + _scheduleCursorCoordsFlush(); +}); + +// ===================== OwnShip ===================== +const ownShipIconSize=[22,48], ownShipAnchor=[11,48]; +const iconOwnShip = L.icon({ iconUrl:'/svg/SVG/ownShip.svg', iconSize:ownShipIconSize, iconAnchor:ownShipAnchor, popupAnchor:[0,-48], className:'ownship-icon' }); +let ownShipMarker=null, ownShipSource='nmea', followMode=false; +let nmeaGps=null, phoneGps=null, phoneWatchId=null, phoneGpsError=null; +let phoneCompassHeading=null, phoneCompassOk=false, phoneCompassListenerAdded=false; +let phoneGpsSmoothed=null; +let _lastFollowTs=0; +let _lastFollowLatLng=null; + +function setOwnShipSource(src) { + ownShipSource = (src === 'phone') ? 'phone' : 'nmea'; + if (ownShipSource === 'phone') startPhoneGps(); + else stopPhoneGps(); + sSet('ownShipSource', ownShipSource); + try { if (typeof window._reflectGpsSourceUi === 'function') window._reflectGpsSourceUi(); } catch(_) {} + updateOwnShipDisplay(); +} +try { + const savedSrc = sGet('ownShipSource', ''); + if (savedSrc === 'phone' || savedSrc === 'nmea') ownShipSource = savedSrc; +} catch (e) {} + +function normalizeDeg(d){ + if(d==null || isNaN(d)) return null; + d = d % 360; + if(d < 0) d += 360; + return d; +} + +function lowPassAngleDeg(prev, next, alpha){ + if (prev == null) return normalizeDeg(next); + const p = normalizeDeg(prev), n = normalizeDeg(next); + if (p == null || n == null) return n; + // shortest direction around 0..360 + let diff = n - p; + if (diff > 180) diff -= 360; + if (diff < -180) diff += 360; + return normalizeDeg(p + diff * alpha); +} + +function haversineMeters(a, b){ + const R = 6371000; + const φ1 = a.lat * Math.PI/180, φ2 = b.lat * Math.PI/180; + const dφ = (b.lat - a.lat) * Math.PI/180; + const dλ = (b.lng - a.lng) * Math.PI/180; + const s = Math.sin(dφ/2)**2 + Math.cos(φ1)*Math.cos(φ2)*Math.sin(dλ/2)**2; + return 2 * R * Math.asin(Math.sqrt(s)); +} + +function pollOwnShip() { + // Источник — ownship.update через WebSocket (заполняется в AisHub.ownship). + try { + const d = AisHub.ownship; + if (d && d.lat != null && d.lon != null) nmeaGps = d; + } catch (e) {} +} + +function startPhoneGps() { + phoneGpsError=null; + if (!window.isSecureContext) { phoneGpsError='Требуется HTTPS. Откройте https://'; return; } + if (!navigator.geolocation) { phoneGpsError='Geolocation API недоступен'; return; } + if (phoneWatchId!=null) return; + startPhoneCompass(); + phoneWatchId=navigator.geolocation.watchPosition( + pos=>{ + phoneGpsError=null; + const raw = { + lat: pos.coords.latitude, + lon: pos.coords.longitude, + accuracy_m: pos.coords.accuracy, + speed: pos.coords.speed!=null ? pos.coords.speed*1.94384 : null, // m/s -> kn + course: normalizeDeg(pos.coords.heading), + heading: normalizeDeg(pos.coords.heading), + compass: normalizeDeg(phoneCompassHeading), + satellites: null, + timestamp: Math.floor(pos.timestamp/1000), + source:'phone' + }; + + // Smooth position slightly to reduce jitter/“рывки” + const acc = raw.accuracy_m != null ? raw.accuracy_m : Infinity; + const alpha = acc <= 10 ? 0.35 : acc <= 25 ? 0.25 : acc <= 60 ? 0.15 : 0.0; + if (!phoneGpsSmoothed || alpha === 0.0) { + phoneGpsSmoothed = {lat: raw.lat, lon: raw.lon}; + } else { + phoneGpsSmoothed.lat = phoneGpsSmoothed.lat + (raw.lat - phoneGpsSmoothed.lat) * alpha; + phoneGpsSmoothed.lon = phoneGpsSmoothed.lon + (raw.lon - phoneGpsSmoothed.lon) * alpha; + } + raw.lat = phoneGpsSmoothed.lat; + raw.lon = phoneGpsSmoothed.lon; + + // Heading: prefer GPS course when moving, else use compass if available + const sp = raw.speed; + const moving = sp != null && !isNaN(sp) && sp >= 1.5; // ~1.5 kn threshold + const h = moving ? raw.course : (raw.compass != null ? raw.compass : raw.course); + raw.heading = h; + + phoneGps = raw; + }, + err=>{ phoneGpsError={1:'Доступ к GPS запрещён',2:'Не удалось определить',3:'Таймаут GPS'}[err.code]||err.message; }, + {enableHighAccuracy:true,maximumAge:1000,timeout:15000} + ); +} +function stopPhoneGps() { + if(phoneWatchId!=null){navigator.geolocation.clearWatch(phoneWatchId);phoneWatchId=null;} + phoneGpsError=null; + phoneGpsSmoothed=null; + stopPhoneCompass(); +} + +const OWN_SHIP_SPEED_VECTOR_SECONDS = 360; +const OWN_SHIP_SPEED_VECTOR_TICK_SECONDS = 60; +const OWN_SHIP_HEADING_LINE_M = 130; +const OWN_SHIP_BEAM_LINE_M = 70; +let ownShipHeadingLine = null; +let ownShipBeamLine = null; +let ownShipSpeedVectorShadow = null; +let ownShipSpeedVector = null; +let ownShipSpeedTicks = []; + +function _removeLayerSafe(layer) { + if (layer) { + try { map.removeLayer(layer); } catch (_) {} + } +} + +function clearOwnShipMotionOverlays() { + _removeLayerSafe(ownShipHeadingLine); ownShipHeadingLine = null; + _removeLayerSafe(ownShipBeamLine); ownShipBeamLine = null; + _removeLayerSafe(ownShipSpeedVectorShadow); ownShipSpeedVectorShadow = null; + _removeLayerSafe(ownShipSpeedVector); ownShipSpeedVector = null; + for (const tick of ownShipSpeedTicks) _removeLayerSafe(tick); + ownShipSpeedTicks = []; +} + +function _ensureOwnShipPolyline(current, opts) { + if (current) return current; + return L.polyline([], Object.assign({ renderer: _overlayCanvasRenderer, interactive: false }, opts)).addTo(map); +} + +function _setOwnShipTickCount(count) { + while (ownShipSpeedTicks.length < count) { + ownShipSpeedTicks.push(L.polyline([], { + renderer: _overlayCanvasRenderer, + color: '#f0f6fc', + weight: 2, + opacity: 0.9, + interactive: false, + }).addTo(map)); + } + while (ownShipSpeedTicks.length > count) { + _removeLayerSafe(ownShipSpeedTicks.pop()); + } +} + +function updateOwnShipMotionOverlays(data, headingDeg) { + if (!data || data.lat == null || data.lon == null) { + clearOwnShipMotionOverlays(); + return; + } + const lat = _numOrNull(data.lat); + const lon = _numOrNull(data.lon); + if (lat == null || lon == null) { + clearOwnShipMotionOverlays(); + return; + } + const origin = [lat, lon]; + const hd = normalizeDeg(headingDeg); + if (hd != null) { + const headEnd = destPointMeters(lat, lon, hd, OWN_SHIP_HEADING_LINE_M); + const beamA = destPointMeters(lat, lon, normalizeDeg(hd - 90), OWN_SHIP_BEAM_LINE_M / 2); + const beamB = destPointMeters(lat, lon, normalizeDeg(hd + 90), OWN_SHIP_BEAM_LINE_M / 2); + ownShipHeadingLine = _ensureOwnShipPolyline(ownShipHeadingLine, { + color: '#f0f6fc', + weight: 2, + opacity: 0.95, + lineCap: 'round', + }); + ownShipHeadingLine.setLatLngs([origin, [headEnd.lat, headEnd.lon]]); + ownShipBeamLine = _ensureOwnShipPolyline(ownShipBeamLine, { + color: '#c9d1d9', + weight: 2, + opacity: 0.8, + lineCap: 'round', + }); + ownShipBeamLine.setLatLngs([[beamA.lat, beamA.lon], [beamB.lat, beamB.lon]]); + } else { + _removeLayerSafe(ownShipHeadingLine); ownShipHeadingLine = null; + _removeLayerSafe(ownShipBeamLine); ownShipBeamLine = null; + } + + const cog = normalizeDeg(data.course); + const sog = _numOrNull(data.speed); + if (cog == null || sog == null || sog < 0.2) { + _removeLayerSafe(ownShipSpeedVectorShadow); ownShipSpeedVectorShadow = null; + _removeLayerSafe(ownShipSpeedVector); ownShipSpeedVector = null; + _setOwnShipTickCount(0); + return; + } + const meters = sog * 1852 / 3600 * OWN_SHIP_SPEED_VECTOR_SECONDS; + const end = destPointMeters(lat, lon, cog, meters); + const pts = [origin, [end.lat, end.lon]]; + ownShipSpeedVectorShadow = _ensureOwnShipPolyline(ownShipSpeedVectorShadow, { + color: '#0d1117', + weight: 6, + opacity: 0.75, + dashArray: '12 10', + lineCap: 'butt', + }); + ownShipSpeedVectorShadow.setLatLngs(pts); + ownShipSpeedVector = _ensureOwnShipPolyline(ownShipSpeedVector, { + color: '#d2ff1a', + weight: 3, + opacity: 0.95, + dashArray: '12 10', + lineCap: 'butt', + }); + ownShipSpeedVector.setLatLngs(pts); + + const tickCount = Math.floor(OWN_SHIP_SPEED_VECTOR_SECONDS / OWN_SHIP_SPEED_VECTOR_TICK_SECONDS); + _setOwnShipTickCount(tickCount); + const tickHalfM = 12; + const mps = sog * 1852 / 3600; + for (let i = 0; i < ownShipSpeedTicks.length; i++) { + const distM = mps * OWN_SHIP_SPEED_VECTOR_TICK_SECONDS * (i + 1); + const p = destPointMeters(lat, lon, cog, distM); + const a = destPointMeters(p.lat, p.lon, normalizeDeg(cog - 90), tickHalfM); + const b = destPointMeters(p.lat, p.lon, normalizeDeg(cog + 90), tickHalfM); + ownShipSpeedTicks[i].setLatLngs([[a.lat, a.lon], [b.lat, b.lon]]); + } +} + +// Keep a handle to the Generic Sensor API fallback so we can stop it. +let _absOrientSensor = null; + +function startPhoneCompass() { + if (phoneCompassListenerAdded) return; + phoneCompassListenerAdded = true; + + const addLegacy = () => { + window.addEventListener('deviceorientationabsolute', onDeviceOrientation, true); + window.addEventListener('deviceorientation', onDeviceOrientation, true); + }; + + // 1) Prefer Android-friendly Generic Sensor API when available. + // AbsoluteOrientationSensor gives true-north-referenced quaternion on Android + // (chrome://flags / modern Android Chrome have this enabled by default). + async function tryAbsoluteOrientationSensor() { + if (!('AbsoluteOrientationSensor' in window)) return false; + try { + // Permissions API (best-effort): some platforms require explicit grants. + if (navigator.permissions && navigator.permissions.query) { + try { + const res = await Promise.all([ + navigator.permissions.query({ name: 'accelerometer' }), + navigator.permissions.query({ name: 'magnetometer' }), + navigator.permissions.query({ name: 'gyroscope' }), + ]); + if (res.some(r => r && r.state === 'denied')) return false; + } catch (_) { /* some UAs don't know these names — just try */ } + } + const s = new window.AbsoluteOrientationSensor({ frequency: 30, referenceFrame: 'screen' }); + s.addEventListener('reading', () => { + try { + const q = s.quaternion; + if (!q || q.length !== 4) return; + // Convert quaternion → heading (rotation about Z, compass = 360 - yaw) + const [x, y, z, w] = q; + // yaw (Z-axis rotation) in radians + const siny_cosp = 2 * (w * z + x * y); + const cosy_cosp = 1 - 2 * (y * y + z * z); + let yaw = Math.atan2(siny_cosp, cosy_cosp); + let deg = yaw * 180 / Math.PI; + // Normalize to compass heading (CW from north) + let heading = (360 - deg) % 360; + if (heading < 0) heading += 360; + phoneCompassHeading = lowPassAngleDeg(phoneCompassHeading, heading, 0.25); + } catch (_) {} + }); + s.addEventListener('error', () => { /* fall through to legacy */ addLegacy(); }); + s.start(); + _absOrientSensor = s; + phoneCompassOk = true; + return true; + } catch (_) { + return false; + } + } + + // 2) iOS 13+ requires explicit permission for DeviceOrientationEvent. + const DOE = window.DeviceOrientationEvent; + if (DOE && typeof DOE.requestPermission === 'function') { + phoneCompassOk = false; + phoneCompassHeading = null; + const once = async () => { + document.removeEventListener('click', once, true); + try { + const r = await DOE.requestPermission(); + if (r === 'granted') { + const ok = await tryAbsoluteOrientationSensor(); + if (!ok) addLegacy(); + phoneCompassOk = true; + } + } catch(e) {} + }; + document.addEventListener('click', once, true); + return; + } + + // 3) Non-iOS path: try Generic Sensor API first, fall back to deviceorientation. + tryAbsoluteOrientationSensor().then(ok => { + if (!ok) { + phoneCompassOk = true; + addLegacy(); + } + }); +} + +function stopPhoneCompass() { + if (_absOrientSensor) { + try { _absOrientSensor.stop(); } catch (_) {} + _absOrientSensor = null; + } + if (!phoneCompassOk) { phoneCompassHeading = null; return; } + window.removeEventListener('deviceorientationabsolute', onDeviceOrientation, true); + window.removeEventListener('deviceorientation', onDeviceOrientation, true); + phoneCompassHeading = null; + phoneCompassOk = false; + phoneCompassListenerAdded = false; +} + +function onDeviceOrientation(e) { + // alpha is 0..360 deg; in many browsers it's “compass heading” when absolute + let a = null; + if (typeof e.webkitCompassHeading === 'number') { + a = e.webkitCompassHeading; // iOS + } else if (typeof e.alpha === 'number') { + // On a number of browsers/devices `alpha` grows counter-clockwise, which makes map rotation feel inverted. + // Convert to a compass-like clockwise heading. + a = 360 - e.alpha; + } + if (a == null) return; + phoneCompassHeading = lowPassAngleDeg(phoneCompassHeading, a, 0.22); +} + +function setOwnShipRotation(mk,hd) { + const h = (hd != null && !isNaN(hd)) ? normalizeDeg(hd) : null; + + // Desired behavior: + // - When map is rotated by compass (heading-up), keep the ownship marker fixed "up" on screen. + // - Otherwise, rotate the marker to show heading. + const desiredDeg = (rotateMapByCompass ? 0 : (h != null ? h : 0)); + + // If leaflet-rotate is present, use its supported per-marker rotation option. + // Plugin expects radians. + try { + if (map && typeof map.getBearing === 'function' && mk && mk.options) { + mk.options.rotation = (desiredDeg * Math.PI / 180); + if (typeof mk.update === 'function') mk.update(); + return; + } + } catch (e) {} + + // Fallback (no rotate plugin): manual CSS rotation for the icon. + const el = mk._icon; if(!el) return; + mk._rotationHeading = desiredDeg; + if(!el.dataset.osO){ el.style.transformOrigin=ownShipAnchor[0]+'px '+ownShipAnchor[1]+'px'; el.dataset.osO='1'; + if(!mk._origSetPos){mk._origSetPos=mk._setPos; mk._setPos=function(p){this._origSetPos.call(this,p);const ic=this._icon;if(ic&&this._rotationHeading!=null){const m=(ic.style.transform||'').match(/translate3d\([^)]+\)/);if(m)ic.style.transform=m[0]+' rotate('+this._rotationHeading+'deg)';}};} + } + const t=(el.style.transform||'').match(/translate3d\([^)]+\)/); + if(t) el.style.transform=desiredDeg!=null?t[0]+' rotate('+desiredDeg+'deg)':t[0]; + else if(desiredDeg!=null) el.style.transform='rotate('+desiredDeg+'deg)'; +} + +function updateOwnShipDisplay() { + try { _initOwnshipCompassUiOnce(); } catch (e) {} + const data=ownShipSource==='phone'?phoneGps:nmeaGps; + const src=document.getElementById('os-source'),co=document.getElementById('os-coords'),cr=document.getElementById('os-course'),sp=document.getElementById('os-speed'),sa=document.getElementById('os-sats'); + if(!data||data.lat==null||data.lon==null){ + if(ownShipSource==='phone'&&phoneGpsError){src.textContent=phoneGpsError;src.style.color='#f85149';} + else{src.textContent=ownShipSource==='phone'?'GPS телефона (ожидание...)':'Внутр. GPS (нет данных)';src.style.color='';} + co.textContent='-';cr.textContent='-';sp.textContent='-';sa.textContent='-'; + _lastOwnshipHeadingForRotate = null; + _setOwnshipCompassUi(); + if (rotateMapByCompass) _applyMapRotation(); + if(ownShipMarker){map.removeLayer(ownShipMarker);ownShipMarker=null;} + clearOwnShipMotionOverlays(); + return; + } + src.style.color=''; src.textContent=ownShipSource==='phone'?'GPS телефона':'Внутр. GPS (NMEA)'; + co.textContent=data.lat.toFixed(6)+', '+data.lon.toFixed(6); + cr.textContent=data.course!=null?data.course.toFixed(1)+'°':'-'; + sp.textContent=data.speed!=null?fmtSpeed(data.speed):'-'; + sa.textContent=data.satellites!=null?data.satellites:'-'; + // Prefer true heading when available; course can be misleading when drifting / low speed. + const hd = (data.heading != null && !isNaN(data.heading)) ? data.heading + : (data.course != null && !isNaN(data.course)) ? data.course + : null; + // Compass + map rotation (heading-up mode) + _lastOwnshipHeadingForRotate = (hd != null && !isNaN(hd)) ? normalizeDeg(hd) : null; + _setOwnshipCompassUi(); + if (rotateMapByCompass) _applyMapRotation(); + if(!ownShipMarker){ ownShipMarker=L.marker([data.lat,data.lon],{icon:iconOwnShip,zIndexOffset:2000}).addTo(map).bindPopup('Своё судно'); requestAnimationFrame(()=>setOwnShipRotation(ownShipMarker,hd)); } + else{ const c=ownShipMarker.getLatLng(); if(Math.abs(c.lat-data.lat)>1e-6||Math.abs(c.lng-data.lon)>1e-6) ownShipMarker.setLatLng([data.lat,data.lon]); setOwnShipRotation(ownShipMarker,hd); } + updateOwnShipMotionOverlays(data, hd); + if(followMode){ + const now = Date.now(); + const graceUntil = (typeof window._followUserPanGraceUntil === 'function') ? window._followUserPanGraceUntil() : 0; + if (now >= graceUntil) { + const ll = L.latLng(data.lat, data.lon); + const minDt = 350; // ms + const minMoveM = 2.5; + const canTime = (now - _lastFollowTs) >= minDt; + const canMove = !_lastFollowLatLng || haversineMeters(_lastFollowLatLng, ll) >= minMoveM; + if (canTime && canMove) { + _lastFollowTs = now; + _lastFollowLatLng = ll; + map.panTo(ll, {animate: true, duration: 0.25}); + } + } + } +} + +// ===== GPS source + Follow (navigator) — integrated into #map-controls ===== +(function initMapControlsGpsFollow(){ + const gpsBtn = document.getElementById('mc-gps-src'); + const followBtn = document.getElementById('mc-follow'); + const iconNmea = gpsBtn ? gpsBtn.querySelector('.mc-gps-icon-nmea') : null; + const iconPhone = gpsBtn ? gpsBtn.querySelector('.mc-gps-icon-phone') : null; + + function reflectGpsSourceUi() { + if (!gpsBtn) return; + gpsBtn.dataset.src = ownShipSource; + gpsBtn.classList.toggle('active', ownShipSource === 'phone'); + if (iconNmea) iconNmea.style.display = (ownShipSource === 'nmea') ? '' : 'none'; + if (iconPhone) iconPhone.style.display = (ownShipSource === 'phone') ? '' : 'none'; + gpsBtn.title = 'Источник GPS: ' + (ownShipSource === 'phone' ? 'телефон (переключить на внутренний NMEA)' : 'внутренний NMEA (переключить на телефон)'); + } + window._reflectGpsSourceUi = reflectGpsSourceUi; + + try { setOwnShipSource(ownShipSource); } catch (e) {} + reflectGpsSourceUi(); + + if (gpsBtn) { + gpsBtn.addEventListener('click', function(){ + const next = (ownShipSource === 'phone') ? 'nmea' : 'phone'; + setOwnShipSource(next); + reflectGpsSourceUi(); + }); + } + + // ---- Follow / Navigator mode ---- + // State: auto-return to ship after user pan; auto-zoom depending on speed. + let _followUserPanUntil = 0; // "thaw" timestamp — do not recentre for this many ms after user drag + let _followAutoZoomLast = null; // last zoom applied by speed logic + let _followProgrammaticMove = false; // set during our own panTo/setView so dragstart/zoomend don't turn off + + function speedToZoom(sogKn) { + if (sogKn == null || isNaN(sogKn) || sogKn < 0) return 15; + if (sogKn < 1) return 16; + if (sogKn < 4) return 15; + if (sogKn < 10) return 14; + if (sogKn < 18) return 13; + if (sogKn < 26) return 12; + return 11; + } + + function setFollow(on) { + followMode = !!on; + if (followBtn) followBtn.classList.toggle('active', followMode); + if (followMode) { + _followUserPanUntil = 0; + updateOwnShipDisplay(); + try { maybeApplyFollowZoom(true); } catch(_) {} + } + } + window._setFollowMode = setFollow; + + function maybeApplyFollowZoom(force) { + if (!followMode) return; + const d = ownShipSource === 'phone' ? phoneGps : nmeaGps; + if (!d || d.lat == null || d.lon == null) return; + const desired = speedToZoom(d.speed); + const cur = map.getZoom(); + // Only act when difference is meaningful, or when forced. + if (force || _followAutoZoomLast == null || Math.abs(cur - desired) >= 1) { + if (cur !== desired) { + _followProgrammaticMove = true; + try { map.setZoom(desired, { animate: true }); } catch(_) {} + setTimeout(() => { _followProgrammaticMove = false; }, 300); + } + _followAutoZoomLast = desired; + } + } + // Re-evaluate zoom periodically while follow is on + setInterval(() => { try { maybeApplyFollowZoom(false); } catch(_) {} }, 4000); + + if (followBtn) { + followBtn.addEventListener('click', function(){ setFollow(!followMode); }); + } + + // When user drags the map, pause auto-recentre for ~4s (navigator-like behaviour: + // you can glance around and it snaps back). A deliberate long pan still keeps follow + // on, it just cools down a bit. + map.on('dragstart', () => { + if (_followProgrammaticMove) return; + if (followMode) _followUserPanUntil = Date.now() + 4000; + }); + map.on('zoomstart', () => { + if (_followProgrammaticMove) return; + // User-initiated zoom pauses auto-zoom for ~20s so manual override sticks. + if (followMode) _followAutoZoomLast = map.getZoom(); + }); + + // Hook pan behaviour into updateOwnShipDisplay by patching the check via a flag. + window._followUserPanGraceUntil = () => _followUserPanUntil; +})(); + +// ===================== Sidebar / OwnShip panel sizing ===================== +function adjustSidebarHeight() { + const sidebar = document.getElementById('sidebar'); + const ownship = document.getElementById('ownship-panel'); + const container = document.getElementById('page-map'); + if (!sidebar || !ownship || !container || window.innerWidth <= 600) { + sidebar.style.maxHeight = ''; + return; + } + const containerH = container.clientHeight; + const ownshipH = ownship.offsetHeight; + sidebar.style.maxHeight = Math.max(120, containerH - ownshipH - 24) + 'px'; +} +window.addEventListener('resize', adjustSidebarHeight); + +// ===================== Mobile panel tabs ===================== +function updateMobCursorCoords() { + // Same breakpoint as CSS: mobile width OR small height on touch devices + const isMobileLayout = (window.innerWidth <= 600) || (window.matchMedia && window.matchMedia('(max-height:520px) and (pointer:coarse)').matches); + if (!isMobileLayout) { document.getElementById('cursor-coords').style.bottom = ''; return; } + const cc = document.getElementById('cursor-coords'); + const sb = document.getElementById('sidebar'); + const os = document.getElementById('ownship-panel'); + const isLandscapeCompact = window.matchMedia && window.matchMedia('(max-height:520px) and (pointer:coarse)').matches; + if (isLandscapeCompact) { + // In landscape mode panels are on the right, keep coords near bottom-left. + cc.style.bottom = '8px'; + return; + } + const bar = document.getElementById('mob-panel-bar'); + const barH = bar ? Math.round(bar.getBoundingClientRect().height || 0) : 0; + let panelH = 0; + if (sb.classList.contains('mob-open')) panelH = sb.offsetHeight; + else if (os.classList.contains('mob-open')) panelH = os.offsetHeight; + cc.style.bottom = (barH + panelH + 8) + 'px'; +} +(function() { + const tabs = document.querySelectorAll('.mob-panel-tab'); + const sidebarEl = document.getElementById('sidebar'); + const ownshipEl = document.getElementById('ownship-panel'); + const barEl = document.getElementById('mob-panel-bar'); + + function closeMobPanels() { + tabs.forEach(t => t.classList.remove('active')); + sidebarEl.classList.remove('mob-open'); + ownshipEl.classList.remove('mob-open'); + setTimeout(() => { try { map.invalidateSize(); } catch (_) {} updateMobCursorCoords(); }, 50); + } + + // Allow drag-to-close on the mobile bar (prevents instinctive "pull to refresh"). + try { + if (barEl) barEl.style.touchAction = 'none'; + } catch (e) {} + (function installBarDragToClose() { + if (!barEl) return; + let dragging = false; + let startX = 0, startY = 0; + let lastX = 0, lastY = 0; + let pointerId = null; + + const isLandscapeCompact = () => (window.matchMedia && window.matchMedia('(max-height:520px) and (pointer:coarse)').matches); + const thresholdPx = 26; + + function anyPanelOpen() { + return (sidebarEl && sidebarEl.classList.contains('mob-open')) || (ownshipEl && ownshipEl.classList.contains('mob-open')); + } + + barEl.addEventListener('pointerdown', (e) => { + try { e.preventDefault(); } catch (_) {} + if (!anyPanelOpen()) return; // nothing to close + dragging = true; + pointerId = e.pointerId; + startX = lastX = e.clientX; + startY = lastY = e.clientY; + try { barEl.setPointerCapture(pointerId); } catch (_) {} + }, { passive: false }); + + barEl.addEventListener('pointermove', (e) => { + if (!dragging || (pointerId != null && e.pointerId !== pointerId)) return; + try { e.preventDefault(); } catch (_) {} + lastX = e.clientX; + lastY = e.clientY; + const dx = lastX - startX; + const dy = lastY - startY; + if (isLandscapeCompact()) { + // Bar is on the right; drag further right to close. + if (dx > thresholdPx) { + dragging = false; + closeMobPanels(); + } + } else { + // Bar is at the bottom; drag down to close. + if (dy > thresholdPx) { + dragging = false; + closeMobPanels(); + } + } + }, { passive: false }); + + function endDrag(e) { + if (!dragging) return; + if (pointerId != null && e && e.pointerId !== pointerId) return; + dragging = false; + pointerId = null; + } + + barEl.addEventListener('pointerup', endDrag, { passive: true }); + barEl.addEventListener('pointercancel', endDrag, { passive: true }); + window.addEventListener('blur', () => { dragging = false; pointerId = null; }); + })(); + + // Swipe-down-to-close on the panels themselves (the "sheet" UX). + // Only the top grip zone initiates a swipe — otherwise the user can scroll the list inside. + (function installSheetSwipeToClose() { + const GRIP_ZONE_PX = 26; + const CLOSE_THRESHOLD_PX = 64; + const isLandscapeCompact = () => (window.matchMedia && window.matchMedia('(max-height:520px) and (pointer:coarse)').matches); + + function install(el) { + if (!el) return; + let dragging = false, startX = 0, startY = 0, pointerId = null, curDelta = 0; + + function reset(animated) { + el.classList.remove('mob-swiping'); + if (animated) { + el.style.transition = 'transform .22s ease'; + } else { + el.style.transition = ''; + } + el.style.transform = ''; + setTimeout(() => { try { el.style.transition = ''; } catch(_) {} }, 260); + } + + el.addEventListener('pointerdown', (e) => { + if (!el.classList.contains('mob-open')) return; + const rect = el.getBoundingClientRect(); + const localY = e.clientY - rect.top; + const localX = e.clientX - rect.left; + // Restrict starting zone to the top grip (portrait) or right grip (landscape). + if (isLandscapeCompact()) { + if (localX > GRIP_ZONE_PX) return; // right-side bar means the grip is on the LEFT edge of the panel itself + } else { + if (localY > GRIP_ZONE_PX) return; + } + dragging = true; + pointerId = e.pointerId; + startX = e.clientX; startY = e.clientY; + curDelta = 0; + el.classList.add('mob-swiping'); + try { el.setPointerCapture(pointerId); } catch(_) {} + }, { passive: true }); + + el.addEventListener('pointermove', (e) => { + if (!dragging || (pointerId != null && e.pointerId !== pointerId)) return; + const dy = e.clientY - startY; + const dx = e.clientX - startX; + if (isLandscapeCompact()) { + if (dx > 0) { + curDelta = dx; + el.style.transform = 'translateX(' + dx + 'px)'; + } + } else { + if (dy > 0) { + curDelta = dy; + el.style.transform = 'translateY(' + dy + 'px)'; + } + } + }, { passive: true }); + + function endSwipe(e) { + if (!dragging) return; + if (pointerId != null && e && e.pointerId !== pointerId) return; + dragging = false; + pointerId = null; + if (curDelta > CLOSE_THRESHOLD_PX) { + // Animate off-screen then close + el.style.transition = 'transform .18s ease-in'; + el.style.transform = isLandscapeCompact() ? 'translateX(120%)' : 'translateY(120%)'; + setTimeout(() => { + closeMobPanels(); + reset(false); + }, 170); + } else { + reset(true); + } + curDelta = 0; + } + el.addEventListener('pointerup', endSwipe, { passive: true }); + el.addEventListener('pointercancel', endSwipe, { passive: true }); + } + + install(sidebarEl); + install(ownshipEl); + })(); + + tabs.forEach(tab => { + tab.addEventListener('click', () => { + const panel = tab.dataset.panel; + const wasActive = tab.classList.contains('active'); + tabs.forEach(t => t.classList.remove('active')); + sidebarEl.classList.remove('mob-open'); + ownshipEl.classList.remove('mob-open'); + if (!wasActive) { + tab.classList.add('active'); + if (panel === 'sidebar') sidebarEl.classList.add('mob-open'); + else ownshipEl.classList.add('mob-open'); + } + setTimeout(() => { map.invalidateSize(); updateMobCursorCoords(); }, 50); + }); + }); +})(); +window.addEventListener('resize', updateMobCursorCoords); + +// ===================== Stats ===================== +const AIS_TYPE_NAMES = {1:'Позиция класс A',2:'Позиция класс A',3:'Позиция класс A',5:'Статика класс A',18:'Позиция класс B',19:'Расш. позиция B',24:'Статика класс B'}; +function fmtUptime(s){const h=Math.floor(s/3600),m=Math.floor(s%3600/60),sec=s%60;return (h?h+'ч ':'')+(m?m+'м ':'')+sec+'с';} +function _setStatText(id, text) { + const el = document.getElementById(id); + if (el) el.textContent = text; +} + +function _counterSumByPrefix(counters, prefixes) { + let out = 0; + if (!counters) return 0; + for (const [k, v] of Object.entries(counters)) { + for (const p of prefixes) { + if (k.startsWith(p)) { out += Number(v) || 0; break; } + } + } + return out; +} + +function _renderStatsTableBody(tbodyId, rows) { + const tb = document.getElementById(tbodyId); + if (!tb) return; + tb.innerHTML = ''; + for (const r of rows) { + const tr = document.createElement('tr'); + const v = r.value; + const isErr = !!r.isErr; + tr.innerHTML = + '' + escHtml(r.label) + '' + + '' + escHtml(v == null ? '-' : String(v)) + ''; + tb.appendChild(tr); + } +} + +function updateStats(){ + try{ + const s = AisHub.stats || {}; + const sys = AisHub.sysinfo || {}; + const counters = s.counters || {}; + + // Vessels: считаем по in-memory витрине (ais_hub отдаёт total в state.warmup, но + // "активных" удобнее взять из клиентского Map). + const nowF = Date.now() / 1000; + let active = 0, total = AisHub.vessels.size, classA = 0, classB = 0; + for (const v of AisHub.vessels.values()) { + if (v && v.lat != null && v.lon != null && (nowF - (v.timestamp || 0)) < 600) active++; + if (v && v.vessel_class === 'A') classA++; + else if (v && v.vessel_class === 'B') classB++; + } + _setStatText('st-active', active); + _setStatText('st-total', total); + _setStatText('st-class-a', classA); + _setStatText('st-class-b', classB); + + const aisMessages = _counterSumByPrefix(counters, ['ais_msg_']); + _setStatText('st-ais-msg', aisMessages); + _setStatText('st-gps-msg', counters.state_ownship_updates != null ? counters.state_ownship_updates : '-'); + _setStatText('st-gps-fix', AisHub.ownship && AisHub.ownship.lat != null ? 'Есть' : 'Нет'); + _setStatText('st-uptime', s.uptime_sec != null ? fmtUptime(Math.floor(s.uptime_sec)) : '-'); + _setStatText('st-sys-uptime', sys.sys_uptime != null ? fmtUptime(sys.sys_uptime) : '-'); + + try{ + const sn = sys.server_now != null ? parseInt(sys.server_now, 10) : null; + const el = document.getElementById('st-rx-time'); + if(el){ + if(sn != null && !isNaN(sn)){ + const clientNow = Math.floor(Date.now()/1000); + const d = clientNow - sn; + const sign = d > 0 ? '+' : ''; + el.textContent = fmtTime(sn) + ' (' + sign + d + 'с)'; + } else el.textContent = '-'; + } + }catch(e){} + + _setStatText('st-cpu-temp', sys.cpu_temp != null ? sys.cpu_temp + '°C' : '-'); + _setStatText('st-cpu-load', sys.cpu_percent != null ? sys.cpu_percent + '%' : '-'); + _setStatText('st-mem', sys.mem_total_mb != null ? sys.mem_used_mb + ' / ' + sys.mem_total_mb + ' МБ (' + sys.mem_pct + '%)' : '-'); + + // AIS/NMEA rate: ais_hub шлёт stats.update раз в 5 секунд → считаем rate между снимками. + _setStatText('st-ais-rate', '-'); + _setStatText('st-nmea-rate', '-'); + _setStatText('st-test-mmsi', '-'); + _setStatText('st-test-mmsi-a', '-'); + _setStatText('st-test-mmsi-b', '-'); + + const tb = document.getElementById('st-ais-types'); + if (tb) { + tb.innerHTML = ''; + const byType = {}; + for (const [k, v] of Object.entries(counters)) { + if (k.startsWith('ais_msg_')) { + const n = k.slice('ais_msg_'.length); + byType[n] = v; + } + } + for (const [t, c] of Object.entries(byType).sort((a, b) => b[1] - a[1])) { + const tr = document.createElement('tr'); + tr.innerHTML = '' + t + '' + (AIS_TYPE_NAMES[t] || 'Тип ' + t) + '' + c + ''; + tb.appendChild(tr); + } + } + + // Expanded parsing/error statistics (toggle section). + const parseRows = [ + { key: 'parser_errors', label: 'parser_errors', isErr: true }, + { key: 'parser_checksum_errors', label: 'parser_checksum_errors', isErr: true }, + { key: 'ais_fragment_errors', label: 'ais_fragment_errors', isErr: true }, + { key: 'ais_fragment_timeouts', label: 'ais_fragment_timeouts', isErr: true }, + ].map(x => ({ label: x.label, value: counters[x.key], isErr: x.isErr && (Number(counters[x.key]) || 0) > 0 })); + _renderStatsTableBody('st-parse-errors', parseRows); + + const udpRows = [ + { key: 'ais_udp_datagrams', label: 'ais_udp_datagrams' }, + { key: 'ais_udp_malformed', label: 'ais_udp_malformed', isErr: true }, + { key: 'udp_events_oversize', label: 'udp_events_oversize', isErr: true }, + ].map(x => ({ label: x.label, value: counters[x.key], isErr: x.isErr && (Number(counters[x.key]) || 0) > 0 })); + _renderStatsTableBody('st-udp-errors', udpRows); + + const storageRows = [ + { key: 'storage_batches', label: 'storage_batches' }, + { key: 'storage_rows', label: 'storage_rows' }, + { key: 'storage_errors', label: 'storage_errors', isErr: true }, + { key: 'storage_last_batch', label: 'storage_last_batch (gauge)' }, + ].map(x => { + const v = x.key === 'storage_last_batch' ? (s.gauges || {}).storage_last_batch : counters[x.key]; + return { label: x.label, value: v, isErr: x.isErr && (Number(v) || 0) > 0 }; + }); + _renderStatsTableBody('st-storage-errors', storageRows); + + const miscRows = [ + { key: 'ws_clients', label: 'ws_clients (gauge)' }, + { key: 'ws_clients_total', label: 'ws_clients_total' }, + { key: 'supervisor_restarts_ingest_ais_udp', label: 'supervisor_restarts_ingest_ais_udp', isErr: true }, + { key: 'udp_events_sent', label: 'udp_events_sent' }, + ].map(x => { + const v = x.key === 'ws_clients' ? (s.gauges || {}).ws_clients : counters[x.key]; + return { label: x.label, value: v, isErr: x.isErr && (Number(v) || 0) > 0 }; + }); + _renderStatsTableBody('st-misc-counters', miscRows); + }catch(e){} +} + +// Локальные системные метрики (CPU/temp/mem/uptime) ais_hub не отдаёт — +// тянем отдельно с локального /api/sysinfo, только пока открыта вкладка «Статистика». +async function refreshSysinfo(){ + try { + const r = await fetch('/api/sysinfo'); + const d = await r.json(); + if (d && typeof d === 'object') AisHub.sysinfo = d; + } catch (e) {} +} + +// ===================== TDMA Slots ===================== +const SLOT_MIN_COLS = 30, SLOT_MAX_COLS = 75, SLOT_CELL = 14, SLOT_LABEL_W = 42; +const RSSI_WINDOW_SEC = 60; +let slotsOpen = false; +const slotState = {a: null, b: null}; +const slotsTooltip = document.getElementById('slots-tooltip'); + +document.getElementById('slots-toggle').addEventListener('click', function() { + slotsOpen = !slotsOpen; + document.getElementById('slots-content').classList.toggle('open', slotsOpen); + document.getElementById('slots-arrow').classList.toggle('open', slotsOpen); + if (slotsOpen) updateSlots(); +}); + +function _hexToBase64(hex) { + if (!hex) return ''; + const bin = new Uint8Array(Math.floor(hex.length / 2)); + for (let i = 0; i < bin.length; i++) bin[i] = parseInt(hex.substr(i * 2, 2), 16); + let s = ''; + for (let i = 0; i < bin.length; i++) s += String.fromCharCode(bin[i]); + try { return btoa(s); } catch (e) { return ''; } +} + +function _buildSlotChannelData(ch) { + const UPPER = ch.toUpperCase(); + const occ = AisHub.slots[UPPER]; + const power_history = AisHub.livePower[UPPER] || []; + const rssi_history = AisHub.rssiHistory[UPPER] || []; + const evCutoff = Date.now() / 1000 - 90; + const event_history = (AisHub.signalEvents[UPPER] || []).filter(e => (e.ts || 0) >= evCutoff); + // Если нет данных по слотам, но есть радио/RSSI — всё равно рисуем график. + if (!occ) { + const hasAny = (power_history && power_history.length) || (rssi_history && rssi_history.length) || (event_history && event_history.length); + if (!hasAny) return null; + return { + no_slots: true, + rssi_history, + power_history, + event_history, + }; + } + const detail = AisHub.slotDetail[UPPER]; + const data = Object.assign({}, occ); + // В ais_hub bitmap приходит как hex-строка, а рендер использует base64 (atob). + if (typeof occ.bitmap === 'string' && occ.bitmap.length > 0) { + // heuristic: hex если только [0-9a-f] + if (/^[0-9a-fA-F]+$/.test(occ.bitmap)) data.bitmap = _hexToBase64(occ.bitmap); + } + data.rssi_history = rssi_history; + data.power_history = power_history; + data.event_history = event_history; + if (detail && detail.utc_minute === occ.utc_minute) { + data.detail_signals = detail.signals || {}; + data.detail_timestamp = detail.timestamp; + } + return data; +} + +function updateSlots() { + try { + renderSlotChannel('a', _buildSlotChannelData('A')); + renderSlotChannel('b', _buildSlotChannelData('B')); + } catch(e) { console.error('updateSlots:', e); } +} + +function renderSlotChannel(ch, data) { + const info = document.getElementById('slots-info-' + ch); + const wrap = document.getElementById('slots-wrap-' + ch); + const canvas = document.getElementById('slots-canvas-' + ch); + const rssiCanvas = document.getElementById('slots-rssi-' + ch); + const bar = document.getElementById('slots-bar-' + ch); + const barFill = document.getElementById('slots-bar-fill-' + ch); + if (!data) { + info.innerHTML = 'Нет данных'; + wrap.style.display = 'none'; + rssiCanvas.style.display = 'none'; + bar.style.display = 'none'; + slotState[ch] = null; + return; + } + if (data.no_slots) { + info.innerHTML = 'Нет данных по слотам'; + bar.style.display = 'none'; + wrap.style.display = 'none'; + slotState[ch] = null; + renderRssiChart(ch, data.rssi_history, data.power_history, data.event_history); + return; + } + const free = data.total - data.occupied; + const pct = (data.occupied / data.total * 100).toFixed(1); + const age = Math.floor(Date.now()/1000) - data.timestamp; + const ageStr = age < 60 ? age + 'с назад' : Math.floor(age/60) + 'м назад'; + const utcDate = new Date(data.utc_minute * 60000); + const utcStr = [utcDate.getUTCHours(), utcDate.getUTCMinutes()].map(v=>String(v).padStart(2,'0')).join(':') + ' UTC'; + const nfStr = data.noise_floor_dbm != null ? data.noise_floor_dbm.toFixed(1) + ' dBm' : '-'; + const thStr = data.threshold_dbm != null ? data.threshold_dbm.toFixed(1) + ' dBm' : '-'; + let anchorExtra = ''; + const t0n = data.slot0_unix_ms != null ? Number(data.slot0_unix_ms) : 0; + if (t0n > 0 && t0n <= Number.MAX_SAFE_INTEGER) { + anchorExtra += ' | слот0: ' + new Date(t0n).toISOString().slice(11, 19) + 'Z'; + } + const fon = data.first_occupied_unix_ms != null ? Number(data.first_occupied_unix_ms) : 0; + if (fon > 0 && fon <= Number.MAX_SAFE_INTEGER) { + anchorExtra += ' | 1-й декод: ' + new Date(fon).toISOString().slice(11, 19) + 'Z'; + } + info.textContent = utcStr + ' | Занято: ' + data.occupied + ' (' + pct + '%) | Свободно: ' + free + ' | NF: ' + nfStr + ' | TH: ' + thStr + anchorExtra + ' | ' + ageStr; + + bar.style.display = ''; + const occPct = data.occupied / data.total * 100; + barFill.style.width = occPct + '%'; + barFill.style.background = occPct > 80 ? '#f85149' : occPct > 50 ? '#d29922' : '#238636'; + + renderRssiChart(ch, data.rssi_history, data.power_history, data.event_history); + + wrap.style.display = ''; + const wrapWidth = Math.max(wrap.clientWidth || 0, 320); + const slotCols = Math.max(SLOT_MIN_COLS, Math.min(SLOT_MAX_COLS, Math.floor((wrapWidth - SLOT_LABEL_W) / SLOT_CELL))); + const slotRows = Math.ceil(data.total / slotCols); + canvas.width = SLOT_LABEL_W + slotCols * SLOT_CELL; + canvas.height = slotRows * SLOT_CELL; + const ctx = canvas.getContext('2d'); + const raw = atob(data.bitmap); + const bitmap = new Uint8Array(raw.length); + for (let i = 0; i < raw.length; i++) bitmap[i] = raw.charCodeAt(i); + const detailSignals = data.detail_signals || {}; + slotState[ch] = { + total: data.total, + bitmap: bitmap, + detailSignals: detailSignals, + cols: slotCols, + rows: slotRows, + cell: SLOT_CELL, + labelW: SLOT_LABEL_W, + slot0UnixMs: data.slot0_unix_ms != null ? Number(data.slot0_unix_ms) : 0, + firstOccupiedUnixMs: data.first_occupied_unix_ms != null ? Number(data.first_occupied_unix_ms) : 0, + }; + + ctx.clearRect(0, 0, canvas.width, canvas.height); + + ctx.font = '10px monospace'; + ctx.fillStyle = '#484f58'; + ctx.textAlign = 'right'; + ctx.textBaseline = 'middle'; + for (let r = 0; r < slotRows; r += 5) { + const sec = Math.round(r * slotCols * 60 / data.total); + ctx.fillText(sec + 's', SLOT_LABEL_W - 4, r * SLOT_CELL + SLOT_CELL / 2); + } + + for (let slot = 0; slot < data.total; slot++) { + const occupied = (bitmap[slot >> 3] >> (slot & 7)) & 1; + const x = SLOT_LABEL_W + (slot % slotCols) * SLOT_CELL; + const y = Math.floor(slot / slotCols) * SLOT_CELL; + ctx.fillStyle = occupied ? '#f85149' : '#238636'; + ctx.fillRect(x, y, SLOT_CELL - 1, SLOT_CELL - 1); + } +} + +function renderRssiChart(ch, history, powerHistory, eventHistory) { + const canvas = document.getElementById('slots-rssi-' + ch); + const hasHistory = history && history.length >= 2; + const hasPower = powerHistory && powerHistory.length >= 2; + const hasEvents = eventHistory && eventHistory.length >= 1; + if (!hasHistory && !hasPower && !hasEvents) { canvas.style.display = 'none'; return; } + canvas.style.display = ''; + const cssWidth = Math.max(320, Math.floor(canvas.getBoundingClientRect().width || canvas.parentElement?.clientWidth || 482)); + const cssHeight = 90; + const dpr = window.devicePixelRatio || 1; + canvas.width = Math.floor(cssWidth * dpr); + canvas.height = Math.floor(cssHeight * dpr); + const c = canvas.getContext('2d'); + c.setTransform(1, 0, 0, 1, 0, 0); + c.scale(dpr, dpr); + const W = cssWidth, H = cssHeight; + const pad = {t: 10, b: 16, l: 44, r: 10}; + const pW = W - pad.l - pad.r, pH = H - pad.t - pad.b; + + const latestTs = Math.max( + hasHistory ? history[history.length - 1].ts : -Infinity, + hasPower ? powerHistory[powerHistory.length - 1].ts : -Infinity, + hasEvents ? eventHistory[eventHistory.length - 1].ts : -Infinity, + Date.now() / 1000 + ); + const xMax = latestTs; + const xMin = xMax - RSSI_WINDOW_SEC; + + const visibleHistory = hasHistory ? history.filter(p => p.ts >= xMin && p.ts <= xMax) : []; + const visiblePower = hasPower ? powerHistory.filter(p => p.ts >= xMin && p.ts <= xMax) : []; + const visibleEvents = hasEvents ? eventHistory.filter(ev => ev.ts >= xMin && ev.ts <= xMax) : []; + + let yMin = Infinity, yMax = -Infinity; + if (visibleHistory.length) { + for (const p of visibleHistory) { + if (p.nf != null) { yMin = Math.min(yMin, p.nf); yMax = Math.max(yMax, p.nf); } + if (p.th != null) { yMin = Math.min(yMin, p.th); yMax = Math.max(yMax, p.th); } + } + } + if (visiblePower.length) { + for (const p of visiblePower) { + if (p.power != null) { yMin = Math.min(yMin, p.power); yMax = Math.max(yMax, p.power); } + } + } + if (!isFinite(yMin)) { canvas.style.display = 'none'; return; } + const yPad = Math.max((yMax - yMin) * 0.15, 2); + yMin -= yPad; yMax += yPad; + const xRange = Math.max(xMax - xMin, 1); + + c.clearRect(0, 0, W, H); + c.fillStyle = '#0d1117'; + c.fillRect(pad.l, pad.t, pW, pH); + + c.strokeStyle = '#161b22'; + c.lineWidth = 1; + const gridSteps = 4; + for (let i = 0; i <= gridSteps; i++) { + const gy = pad.t + pH * i / gridSteps; + c.beginPath(); c.moveTo(pad.l, gy); c.lineTo(pad.l + pW, gy); c.stroke(); + } + + function toX(ts) { return pad.l + (ts - xMin) / xRange * pW; } + function toY(v) { return pad.t + (1 - (v - yMin) / (yMax - yMin)) * pH; } + + /* Начало кадра AIS TDMA — граница UTC-минуты */ + c.save(); + c.beginPath(); + c.rect(pad.l, pad.t, pW, pH); + c.clip(); + c.strokeStyle = '#3fb950'; + c.lineWidth = 1; + c.setLineDash([3, 3]); + let frameTs = Math.floor(xMin / 60) * 60; + if (frameTs < xMin) frameTs += 60; + for (; frameTs <= xMax; frameTs += 60) { + const fx = toX(frameTs); + c.beginPath(); + c.moveTo(fx, pad.t); + c.lineTo(fx, pad.t + pH); + c.stroke(); + } + c.setLineDash([]); + c.restore(); + + function drawLine(src, key, color) { + c.save(); + c.beginPath(); + c.rect(pad.l, pad.t, pW, pH); + c.clip(); + c.beginPath(); c.strokeStyle = color; c.lineWidth = 1.5; + let started = false; + for (const p of src) { + if (p[key] == null) continue; + const x = toX(p.ts), y = toY(p[key]); + if (!started) { c.moveTo(x, y); started = true; } else c.lineTo(x, y); + } + c.stroke(); + c.restore(); + } + if (visiblePower.length) drawLine(visiblePower, 'power', '#c678dd'); + if (visibleHistory.length) drawLine(visibleHistory, 'nf', '#4fc3f7'); + if (visibleHistory.length) drawLine(visibleHistory, 'th', '#f0883e'); + + if (visibleEvents.length) { + c.save(); + c.beginPath(); + c.rect(pad.l, pad.t, pW, pH); + c.clip(); + c.strokeStyle = '#ffd166'; + c.fillStyle = '#ffd166'; + c.lineWidth = 1; + c.font = '8px monospace'; + c.textAlign = 'center'; + c.textBaseline = 'top'; + for (const ev of visibleEvents) { + const x = toX(ev.ts); + c.beginPath(); + c.moveTo(x, pad.t); + c.lineTo(x, pad.t + pH); + c.stroke(); + c.fillText(String(ev.slot), x, pad.t + 2); + } + c.restore(); + } + + c.font = '9px monospace'; + c.fillStyle = '#484f58'; + c.textAlign = 'right'; + c.textBaseline = 'top'; + c.fillText(yMax.toFixed(0) + ' dBm', pad.l - 3, pad.t); + c.textBaseline = 'bottom'; + c.fillText(yMin.toFixed(0) + ' dBm', pad.l - 3, pad.t + pH); + const yMid = (yMin + yMax) / 2; + c.textBaseline = 'middle'; + c.fillText(yMid.toFixed(0), pad.l - 3, pad.t + pH / 2); + + c.textAlign = 'left'; c.textBaseline = 'top'; + const t0 = new Date(xMin * 1000), t1 = new Date(xMax * 1000); + const fmt = d => [d.getHours(), d.getMinutes()].map(v=>String(v).padStart(2,'0')).join(':'); + c.fillText(fmt(t0), pad.l, pad.t + pH + 3); + c.textAlign = 'right'; + c.fillText(fmt(t1), pad.l + pW, pad.t + pH + 3); + + const lastHistory = visibleHistory.length ? visibleHistory[visibleHistory.length - 1] : null; + const lastPower = visiblePower.length ? visiblePower[visiblePower.length - 1] : null; + if (lastPower && lastPower.power != null) { + c.fillStyle = '#c678dd'; c.textAlign = 'left'; c.textBaseline = 'bottom'; + c.fillText(lastPower.power.toFixed(1), toX(lastPower.ts) + 3, toY(lastPower.power)); + } + if (lastHistory && lastHistory.nf != null) { + c.fillStyle = '#4fc3f7'; c.textAlign = 'left'; c.textBaseline = 'bottom'; + c.fillText(lastHistory.nf.toFixed(1), toX(lastHistory.ts) + 3, toY(lastHistory.nf)); + } + if (lastHistory && lastHistory.th != null) { + c.fillStyle = '#f0883e'; c.textAlign = 'left'; c.textBaseline = 'top'; + c.fillText(lastHistory.th.toFixed(1), toX(lastHistory.ts) + 3, toY(lastHistory.th)); + } +} + +function buildSlotInfoText(ch, st, slot) { + const occ = (st.bitmap[slot >> 3] >> (slot & 7)) & 1; + const timeSec = (slot * 60 / st.total).toFixed(2); + let text = 'Канал ' + ch.toUpperCase() + ' | Слот ' + slot + ' | +' + timeSec + 'с от начала кадра | ' + (occ ? 'Занят' : 'Свободен'); + const t0 = st.slot0UnixMs; + if (t0 != null && t0 > 0) { + const n = Number(t0); + if (Number.isFinite(n) && n <= Number.MAX_SAFE_INTEGER) { + const slotMs = (60000 * slot) / st.total; + const abs = new Date(n + slotMs); + text += ' | UTC ' + abs.toISOString().replace('T', ' ').replace(/\.\d{3}Z$/, 'Z'); + } + } + if (occ && st.detailSignals && Object.prototype.hasOwnProperty.call(st.detailSignals, slot)) { + text += ' | Signal ' + st.detailSignals[slot].toFixed(1) + ' dB'; + } + return text; +} + +function slotCanvasHover(e, ch) { + const st = slotState[ch]; + if (!st) { slotsTooltip.style.display = 'none'; return; } + const canvas = e.target; + const rect = canvas.getBoundingClientRect(); + const sx = canvas.width / rect.width, sy = canvas.height / rect.height; + const mx = (e.clientX - rect.left) * sx, my = (e.clientY - rect.top) * sy; + const col = Math.floor((mx - st.labelW) / st.cell); + const row = Math.floor(my / st.cell); + if (col < 0 || col >= st.cols || row < 0 || row >= st.rows) { slotsTooltip.style.display = 'none'; return; } + const slot = row * st.cols + col; + if (slot >= st.total) { slotsTooltip.style.display = 'none'; return; } + slotsTooltip.textContent = buildSlotInfoText(ch, st, slot); + slotsTooltip.style.display = ''; + slotsTooltip.style.left = (e.clientX + 14) + 'px'; + slotsTooltip.style.top = (e.clientY - 10) + 'px'; +} +document.getElementById('slots-canvas-a').addEventListener('mousemove', function(e){ slotCanvasHover(e, 'a'); }); +document.getElementById('slots-canvas-b').addEventListener('mousemove', function(e){ slotCanvasHover(e, 'b'); }); +document.getElementById('slots-canvas-a').addEventListener('mouseleave', function(){ slotsTooltip.style.display='none'; }); +document.getElementById('slots-canvas-b').addEventListener('mouseleave', function(){ slotsTooltip.style.display='none'; }); + +// ===================== Test Slot Send ===================== +document.getElementById('test-slot-send').addEventListener('click', async function() { + const btn = this; + const status = document.getElementById('test-slot-status'); + const channel = document.getElementById('test-slot-channel').value; + const slot = parseInt(document.getElementById('test-slot-number').value, 10); + if (isNaN(slot) || slot < 0 || slot > 2249) { + status.textContent = 'Слот 0\u20132249'; + status.className = 'slots-test-status err'; + return; + } + btn.disabled = true; + status.textContent = 'Отправка...'; + status.className = 'slots-test-status wait'; + try { + const r = await fetch('/api/send_test_slot', { + method: 'POST', + headers: {'Content-Type': 'application/json'}, + body: JSON.stringify({channel, slot}) + }); + const data = await r.json(); + if (data.ok) { + status.textContent = 'OK \u2192 ' + data.dest + ' (' + data.size + 'B)'; + status.className = 'slots-test-status ok'; + } else { + status.textContent = data.error || 'Ошибка'; + status.className = 'slots-test-status err'; + } + } catch(e) { + status.textContent = 'Ошибка: ' + e.message; + status.className = 'slots-test-status err'; + } + btn.disabled = false; +}); + +// Click on slot canvas to fill slot number +function slotCanvasClick(e, ch) { + const st = slotState[ch]; + if (!st) return; + const canvas = e.target; + const rect = canvas.getBoundingClientRect(); + const sx = canvas.width / rect.width, sy = canvas.height / rect.height; + const mx = (e.clientX - rect.left) * sx, my = (e.clientY - rect.top) * sy; + const col = Math.floor((mx - st.labelW) / st.cell); + const row = Math.floor(my / st.cell); + if (col < 0 || col >= st.cols || row < 0 || row >= st.rows) return; + const slot = row * st.cols + col; + if (slot >= st.total) return; + document.getElementById('test-slot-number').value = slot; + document.getElementById('test-slot-channel').value = ch === 'a' ? 'A' : 'B'; + document.getElementById('slot-selected-info').textContent = buildSlotInfoText(ch, st, slot); +} +document.getElementById('slots-canvas-a').addEventListener('click', function(e){ slotCanvasClick(e, 'a'); }); +document.getElementById('slots-canvas-b').addEventListener('click', function(e){ slotCanvasClick(e, 'b'); }); +window.addEventListener('resize', () => { if (slotsOpen) updateSlots(); }); + +// ===================== Settings ===================== +document.getElementById('set-server').textContent=location.host; +document.getElementById('set-https').textContent=location.protocol==='https:'?'Да':'Нет'; +document.getElementById('set-secure').textContent=window.isSecureContext?'Да':'Нет'; + +(function initUnitSettings() { + const distSel = document.getElementById('set-dist-unit'); + distSel.value = distUnit; + distSel.addEventListener('change', () => { + distUnit = distSel.value; + sSet('distUnit', distUnit); + document.getElementById('range-value').textContent = fmtRange(getRangeNM()); + }); + const speedSel = document.getElementById('set-speed-unit'); + speedSel.value = speedUnit; + speedSel.addEventListener('change', () => { + speedUnit = speedSel.value; + sSet('speedUnit', speedUnit); + }); +})(); + +(function initDangerRadiusSettings() { + const warnEl = document.getElementById('set-warn-radius'); + const nearEl = document.getElementById('set-near-radius'); + const warnSl = document.getElementById('set-warn-radius-slider'); + const nearSl = document.getElementById('set-near-radius-slider'); + const warnUnit = document.getElementById('set-warn-radius-unit'); + const nearUnit = document.getElementById('set-near-radius-unit'); + if (!warnEl || !nearEl || !warnSl || !nearSl) return; + + const BASE_MAX_NM = 50; + function refreshUiForUnit() { + if (warnUnit) warnUnit.textContent = uiUnitName(); + if (nearUnit) nearUnit.textContent = uiUnitName(); + const maxUi = nmToUi(BASE_MAX_NM); + warnSl.max = String(maxUi); + nearSl.max = String(maxUi); + // keep current NM values, just re-render in UI units + warnEl.value = String(nmToUi(warnRadiusNm || 0)); + nearEl.value = String(nmToUi(nearRadiusNm || 0)); + warnSl.value = warnEl.value; + nearSl.value = nearEl.value; + // update placeholders + warnEl.placeholder = '0 = выкл'; + nearEl.placeholder = '0 = выкл'; + } + + refreshUiForUnit(); + + const apply = () => { + const wUi = parseFloat(warnEl.value); + const nUi = parseFloat(nearEl.value); + const wNm = Number.isFinite(wUi) && wUi > 0 ? uiToNm(wUi) : 0; + const nNm = Number.isFinite(nUi) && nUi > 0 ? uiToNm(nUi) : 0; + warnRadiusNm = wNm; + nearRadiusNm = nNm; + sSet('warnRadiusNm', String(warnRadiusNm)); + sSet('nearRadiusNm', String(nearRadiusNm)); + updateDangerCircles(); + updateDangerBanner(); + }; + const clampStr = (v) => { + const x = parseFloat(v); + if (!Number.isFinite(x) || x < 0) return '0'; + return String(Math.min(parseFloat(warnSl.max || '50'), x)); + }; + const syncWarnFromText = () => { warnEl.value = clampStr(warnEl.value); warnSl.value = warnEl.value; apply(); }; + const syncNearFromText = () => { nearEl.value = clampStr(nearEl.value); nearSl.value = nearEl.value; apply(); }; + const syncWarnFromSlider = () => { warnEl.value = warnSl.value; apply(); }; + const syncNearFromSlider = () => { nearEl.value = nearSl.value; apply(); }; + + warnEl.addEventListener('input', syncWarnFromText); + nearEl.addEventListener('input', syncNearFromText); + warnSl.addEventListener('input', syncWarnFromSlider); + nearSl.addEventListener('input', syncNearFromSlider); + apply(); + + // When distance unit changes, keep NM values but redraw UI in new units + try { + const distSel = document.getElementById('set-dist-unit'); + if (distSel) distSel.addEventListener('change', () => setTimeout(() => { refreshUiForUnit(); }, 0)); + } catch (e) {} +})(); + +// ===================== Network Settings ===================== +let netSelectedMode = null; +const netMsg = document.getElementById('net-msg'); + +function showNetMsg(text, cls) { + netMsg.className = 'net-msg ' + cls; + netMsg.textContent = text; + if (cls === 'ok') setTimeout(() => { netMsg.className = 'net-msg'; }, 5000); +} + +function setNetMode(mode) { + netSelectedMode = mode; + document.getElementById('net-btn-ap').classList.toggle('active', mode === 'ap'); + document.getElementById('net-btn-wifi').classList.toggle('active', mode === 'wifi'); + document.getElementById('net-ap-fields').style.display = mode === 'ap' ? '' : 'none'; + document.getElementById('net-wifi-fields').style.display = mode === 'wifi' ? '' : 'none'; +} +document.getElementById('net-btn-ap').addEventListener('click', () => setNetMode('ap')); +document.getElementById('net-btn-wifi').addEventListener('click', () => setNetMode('wifi')); + +document.getElementById('net-adv-toggle').addEventListener('click', function() { + const adv = document.getElementById('net-advanced'); + const open = adv.classList.toggle('open'); + this.innerHTML = open ? 'Дополнительно ▴' : 'Дополнительно ▾'; +}); + +async function loadNetworkConfig() { + try { + const r = await fetch('/api/network'); + const data = await r.json(); + const cfg = data.config || {}; + const live = data.live || {}; + + const liveMode = document.getElementById('net-live-mode'); + const modeLabel = {ap: 'Точка доступа', wifi: 'WiFi-клиент'}; + liveMode.textContent = modeLabel[live.mode] || live.mode || '?'; + liveMode.className = 'net-status ' + (live.mode || 'unknown'); + + document.getElementById('net-live-ip').textContent = live.ip || '-'; + document.getElementById('net-live-ssid').textContent = live.ssid || '-'; + + setNetMode(cfg.mode || 'ap'); + document.getElementById('net-ap-ssid').value = cfg.ap_ssid || ''; + document.getElementById('net-ap-psk').value = cfg.ap_psk || ''; + document.getElementById('net-ap-ip').value = cfg.ap_ip || ''; + document.getElementById('net-wifi-ssid').value = cfg.wifi_ssid || ''; + document.getElementById('net-wifi-psk').value = cfg.wifi_psk || ''; + document.getElementById('net-wifi-ip').value = cfg.wifi_ip || ''; + document.getElementById('net-wifi-gw').value = cfg.wifi_gw || ''; + document.getElementById('net-wifi-dns').value = cfg.wifi_dns || ''; + document.getElementById('net-iface').value = cfg.iface || 'wlan0'; + } catch(e) { console.error('loadNetworkConfig:', e); } +} + +function collectNetConfig() { + return { + mode: netSelectedMode, + ap_ssid: document.getElementById('net-ap-ssid').value, + ap_psk: document.getElementById('net-ap-psk').value, + ap_ip: document.getElementById('net-ap-ip').value, + wifi_ssid: document.getElementById('net-wifi-ssid').value, + wifi_psk: document.getElementById('net-wifi-psk').value, + wifi_ip: document.getElementById('net-wifi-ip').value, + wifi_gw: document.getElementById('net-wifi-gw').value, + wifi_dns: document.getElementById('net-wifi-dns').value, + iface: document.getElementById('net-iface').value || 'wlan0', + }; +} + +document.getElementById('net-save-btn').addEventListener('click', async function() { + this.disabled = true; + try { + const cfg = collectNetConfig(); + const r = await fetch('/api/network', {method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify(cfg)}); + const data = await r.json(); + if (data.ok) showNetMsg('Настройки сохранены', 'ok'); + else showNetMsg(data.error || 'Ошибка сохранения', 'err'); + } catch(e) { showNetMsg('Ошибка: ' + e.message, 'err'); } + this.disabled = false; +}); + +document.getElementById('net-switch-btn').addEventListener('click', async function() { + const mode = netSelectedMode; + const modeLabel = mode === 'ap' ? 'точку доступа' : 'WiFi-клиент'; + if (!confirm('Переключить на ' + modeLabel + '?\nСоединение может быть потеряно на несколько секунд.')) return; + this.disabled = true; + showNetMsg('Переключение на ' + modeLabel + '...', 'info'); + try { + const cfg = collectNetConfig(); + cfg.mode = mode; + const r = await fetch('/api/network/switch', {method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify(cfg)}); + const data = await r.json(); + if (data.ok) { + showNetMsg('Переключено на ' + modeLabel + '. Обновите страницу по новому адресу.', 'ok'); + setTimeout(loadNetworkConfig, 3000); + } else { + showNetMsg(data.error || 'Ошибка переключения', 'err'); + } + } catch(e) { showNetMsg('Связь потеряна — устройство переключается. Подключитесь к новой сети.', 'info'); } + this.disabled = false; +}); + +document.getElementById('net-scan-btn').addEventListener('click', async function() { + this.disabled = true; + this.textContent = 'Сканирование...'; + const list = document.getElementById('net-scan-list'); + try { + const r = await fetch('/api/network/scan'); + const data = await r.json(); + if (data.ok && data.networks && data.networks.length) { + list.style.display = ''; + list.innerHTML = ''; + for (const n of data.networks) { + const it = document.createElement('div'); + it.className = 'net-scan-item'; + it.innerHTML = '' + n.ssid + '' + (n.signal != null ? n.signal + ' dBm' : '') + ''; + it.addEventListener('click', () => { + document.getElementById('net-wifi-ssid').value = n.ssid; + list.querySelectorAll('.net-scan-item').forEach(e => e.classList.remove('selected')); + it.classList.add('selected'); + }); + list.appendChild(it); + } + } else { + list.style.display = ''; + list.innerHTML = '
' + (data.error || 'Сети не найдены') + '
'; + } + } catch(e) { list.style.display=''; list.innerHTML='
Ошибка сканирования
'; } + this.disabled = false; + this.textContent = 'Сканировать'; +}); + +loadNetworkConfig(); + +// ===================== Logs ===================== +let logLastSeq=0, logAutoscroll=true, logLines=[]; +const logOutput=document.getElementById('log-output'); +const logFilter=document.getElementById('log-filter'); +const logSearch=document.getElementById('log-search'); + +function classifyLine(line){ + const h=(line.split(',')[0]||''); + if((line.startsWith('!')||line.startsWith('$'))&&h.endsWith('VDM')) return 'ais'; + if(/^\$G[PNLA](RMC|GGA)/i.test(line)) return 'gps'; + return 'unknown'; +} +function renderLogs(){ + const filter=logFilter.value; + const search=logSearch.value.toLowerCase(); + const frag=document.createDocumentFragment(); + let count=0; + const visible=logLines.filter(l=>{ + if(filter!=='all'&&l.cls!==filter) return false; + if(search&&!l.line.toLowerCase().includes(search)) return false; + return true; + }).slice(-500); + logOutput.innerHTML=''; + for(const l of visible){ + const div=document.createElement('div'); + div.className='log-line '+l.cls; + div.innerHTML=''+l.time+' '+escHtml(l.line); + frag.appendChild(div); + count++; + } + logOutput.appendChild(frag); + document.getElementById('log-count').textContent=count+' строк'; + if(logAutoscroll) logOutput.scrollTop=logOutput.scrollHeight; +} + +let _logSeenKeys = new Set(); +async function pollLogs(){ + // ais_hub отдаёт последние N строк (ring buffer / SQLite), seq там нет — дедупим по ts+line. + try{ + const r = await fetch('/api/v1/nmea/tail?limit=500'); + const data = await r.json(); + if (!Array.isArray(data) || !data.length) return; + let added = 0; + for (const e of data) { + if (!e || !e.line) continue; + const key = (e.ts || 0).toFixed(3) + '|' + e.line; + if (_logSeenKeys.has(key)) continue; + _logSeenKeys.add(key); + const d = new Date((e.ts || 0) * 1000); + const time = [d.getHours(), d.getMinutes(), d.getSeconds()].map(v => String(v).padStart(2, '0')).join(':'); + logLines.push({ seq: ++logLastSeq, time, line: e.line, cls: classifyLine(e.line) }); + added++; + } + if (added === 0) return; + if (logLines.length > 2000) logLines = logLines.slice(-2000); + if (_logSeenKeys.size > 4000) { + // Подрезаем кеш дедупликации чтоб не рос. + _logSeenKeys = new Set(); + for (const l of logLines) _logSeenKeys.add(((l.line || '') + '|' + (l.time || ''))); + } + if (currentTab === 'logs') renderLogs(); + }catch(e){} +} + +document.getElementById('log-autoscroll').addEventListener('click',function(){logAutoscroll=!logAutoscroll;this.classList.toggle('active',logAutoscroll);}); +document.getElementById('log-clear').addEventListener('click',()=>{logLines=[];renderLogs();}); +logFilter.addEventListener('change',renderLogs); +logSearch.addEventListener('input',renderLogs); + +// ===================== Config Page ===================== +let cfgLoaded = false; +const cfgEditor = document.getElementById('cfg-editor'); +const cfgMsg = document.getElementById('cfg-msg'); +const cfgSvcBadge = document.getElementById('cfg-svc-state'); +const cfgTabMini = document.getElementById('cfg-tab-mini'); +const cfgTabAisHub = document.getElementById('cfg-tab-aishub'); +const cfgFileHint = document.getElementById('cfg-file-hint'); +const cfgBottomHint = document.getElementById('cfg-bottom-hint'); + +let cfgTarget = 'mini'; // 'mini' | 'aishub' +const CFG_TARGETS = { + mini: { name: 'AIS-catcher Mini', file: '/ais-mini.conf', service: 'aisMini.service', url: '/api/config', svcStatus: '/api/service/status', svcRestart: '/api/service/restart' }, + aishub: { name: 'AisHub', file: '/opt/aishub/config/config.yaml', service: 'ais_hub.service', url: '/api/config/aishub', svcStatus: '/api/service/aishub/status', svcRestart: '/api/service/aishub/restart' }, +}; + +function showCfgMsg(text, cls) { + cfgMsg.className = 'config-msg ' + cls; + cfgMsg.textContent = text; + if (cls === 'ok') setTimeout(() => { cfgMsg.className = 'config-msg'; cfgMsg.textContent = ''; }, 5000); +} + +function _cfgMeta() { return CFG_TARGETS[cfgTarget] || CFG_TARGETS.mini; } +function _renderCfgHeader() { + const m = _cfgMeta(); + try { + if (cfgTabMini) cfgTabMini.classList.toggle('active', cfgTarget === 'mini'); + if (cfgTabAisHub) cfgTabAisHub.classList.toggle('active', cfgTarget === 'aishub'); + if (cfgFileHint) cfgFileHint.textContent = 'Файл: ' + m.file; + if (cfgBottomHint) cfgBottomHint.textContent = 'Файл: ' + m.file + ' | Сервис: ' + m.service; + } catch (e) {} +} + +async function loadConfig() { + try { + const r = await fetch(_cfgMeta().url); + const data = await r.json(); + if (data.ok) { + cfgEditor.value = data.text; + cfgLoaded = true; + showCfgMsg('', ''); + } else { + cfgEditor.value = ''; + showCfgMsg(data.error || 'Ошибка загрузки', 'err'); + } + } catch(e) { + cfgEditor.value = ''; + showCfgMsg('Ошибка: ' + e.message, 'err'); + } +} + +async function loadServiceStatus() { + const badge = cfgSvcBadge || document.getElementById('cfg-svc-state'); + try { + const r = await fetch(_cfgMeta().svcStatus); + const data = await r.json(); + const state = data.state || 'unknown'; + badge.textContent = state; + badge.className = 'config-svc-badge ' + (state === 'active' ? 'active' : state === 'inactive' ? 'inactive' : 'unknown'); + } catch(e) { + badge.textContent = '?'; + badge.className = 'config-svc-badge unknown'; + } +} + +document.getElementById('cfg-save-btn').addEventListener('click', async function() { + this.disabled = true; + showCfgMsg('Сохранение...', 'info'); + try { + const r = await fetch(_cfgMeta().url, { + method: 'POST', + headers: {'Content-Type': 'application/json'}, + body: JSON.stringify({text: cfgEditor.value}) + }); + const data = await r.json(); + if (data.ok) showCfgMsg('Конфиг сохранён', 'ok'); + else showCfgMsg(data.error || 'Ошибка сохранения', 'err'); + } catch(e) { + showCfgMsg('Ошибка: ' + e.message, 'err'); + } + this.disabled = false; +}); + +document.getElementById('cfg-restart-btn').addEventListener('click', async function() { + const m = _cfgMeta(); + if (!confirm('Перезапустить ' + m.service + '?\nПриём AIS может быть прерван на несколько секунд.')) return; + this.disabled = true; + showCfgMsg('Перезапуск сервиса...', 'info'); + try { + const r = await fetch(m.svcRestart, { + method: 'POST', + headers: {'Content-Type': 'application/json'} + }); + const data = await r.json(); + if (data.ok) { + showCfgMsg('Сервис перезапущен', 'ok'); + setTimeout(loadServiceStatus, 2000); + } else { + showCfgMsg(data.error || 'Ошибка перезапуска', 'err'); + } + } catch(e) { + showCfgMsg('Ошибка: ' + e.message, 'err'); + } + this.disabled = false; +}); + +document.getElementById('cfg-reload-btn').addEventListener('click', () => { + loadConfig(); + loadServiceStatus(); +}); + +function _setCfgTarget(t) { + cfgTarget = (t === 'aishub') ? 'aishub' : 'mini'; + _renderCfgHeader(); + loadConfig(); + loadServiceStatus(); +} +try { + if (cfgTabMini) cfgTabMini.addEventListener('click', () => _setCfgTarget('mini')); + if (cfgTabAisHub) cfgTabAisHub.addEventListener('click', () => _setCfgTarget('aishub')); + _renderCfgHeader(); +} catch (e) {} + +// ===================== Main loop ===================== +// ===================== Connection status (server reachability) ===================== +let _connState = { offline: false, lastOkTs: 0, lastErrTs: 0, lastMsg: '' }; +function connBannerEl(){ return document.getElementById('conn-banner'); } +function setConnBanner(offline, msg){ + const el = connBannerEl(); + if (!el) return; + const text = msg || (offline ? 'Нет связи с сервером' : 'Связь восстановлена'); + el.textContent = text; + el.classList.toggle('conn-banner--offline', !!offline); + el.classList.toggle('conn-banner--online', !offline); + el.style.display = text ? '' : 'none'; + if (!offline) { + // hide "online" after a short while + setTimeout(() => { + if (!_connState.offline && el.textContent === text) el.style.display = 'none'; + }, 1500); + } +} +function markConnOk(){ + const now = Date.now(); + _connState.lastOkTs = now; + if (_connState.offline) { + _connState.offline = false; + setConnBanner(false, 'Связь восстановлена'); + } +} +function markConnErr(err){ + const now = Date.now(); + _connState.lastErrTs = now; + _connState.lastMsg = (err && err.message) ? err.message : ''; + if (!_connState.offline) { + _connState.offline = true; + setConnBanner(true, 'Нет связи с сервером'); + } +} +window.addEventListener('offline', () => { _connState.offline = true; setConnBanner(true, 'Нет сети (offline)'); }); +window.addEventListener('online', () => { /* actual reachability checked by fetches */ setConnBanner(false, 'Сеть появилась…'); }); + +// ===================== UI tick ===================== +// Данные (vessels/ownship/stats/…) приходят по WebSocket и сразу запускают redraw. +// Этот таймер делает только лёгкие UI-штуки: «возраст» в сайдбаре, danger-banner, +// /api/sysinfo и /api/v1/nmea/tail — если открыты соответствующие вкладки. +function uiTick() { + try { + pollOwnShip(); // скопировать AisHub.ownship → nmeaGps + updateOwnShipDisplay(); // время «ago», followMode и пр. + updateBaseStations(); // TTL/removal for static AIS targets between WS events + updateBuoys(); // TTL/removal for AtoN targets between WS events + + // Sidebar живёт от in-memory витрин. + if (!window._mapInteracting) { + const os = getOwnShipPos(); + const combined = [] + .concat(lastAnyVessels || []) + .concat(lastBaseStations || []) + .concat(lastBuoys || []); + if (os) { + for (const v of combined) { + if (v && v.lat != null && v.lon != null) v._distNM = haversineNM(os.lat, os.lon, v.lat, v.lon); + else v._distNM = null; + } + } else { + for (const v of combined) { if (v) v._distNM = null; } + } + updateSidebar(combined); + adjustSidebarHeight(); + updateDangerCircles(); + updateDangerBanner(); + if (currentTab === 'targets') { + try { renderTargetsTab(); } catch (_) {} + } + } + + if (currentTab === 'stats') { + refreshSysinfo().then(() => updateStats()).catch(() => {}); + } + if (currentTab === 'logs') { + pollLogs(); + } + } catch (e) { + try { console.error('[AISMap] uiTick failed', e); } catch (_) {} + } +} + +setInterval(uiTick, 1000); +uiTick(); +adjustSidebarHeight(); + +// Индикатор связи: считается здоровым, если WS-соединение живо и недавно были события. +setInterval(() => { + try { + const wsOk = AisHub.wsOpen; + const fresh = (Date.now() - (AisHub.lastEventTs || 0)) < 20000; + if (wsOk) { + // Периодически подтверждаем, даже если событий сейчас нет. + if (AisHub.snapshotLoaded) markConnOk(); + } else if (!fresh) { + markConnErr(new Error('ais_hub WS disconnected')); + } + } catch (e) {} +}, 3000); + +// Стартуем WebSocket после полной загрузки страницы, чтобы не рвать рукопожатие +// во время загрузки (Firefox: «соединение … было прервано» у new WebSocket). +function _startAisHubWebSocket() { + try { AisHubWS.open(); } catch (e) { try { console.error('[AISMap] WS open failed', e); } catch (_) {} } +} +if (document.readyState === 'complete') { + _startAisHubWebSocket(); +} else { + window.addEventListener('load', _startAisHubWebSocket, { once: true }); +} + +// Map interaction gating for performance (z14+ pan/zoom). +try { + window._mapInteracting = false; + let _resumeTimer = null; + const setInteracting = (v) => { + window._mapInteracting = !!v; + if (!v) { + if (_resumeTimer) clearTimeout(_resumeTimer); + // Let Leaflet finish settling tiles, then do one catch-up pass. + _resumeTimer = setTimeout(() => { uiTick(); AisHubRedraw.flushNow(); }, 120); + } + }; + map.on('movestart', () => setInteracting(true)); + map.on('moveend', () => setInteracting(false)); + map.on('zoomstart', () => setInteracting(true)); + map.on('zoomend', () => setInteracting(false)); +} catch (e) {} diff --git a/static/js/mmsi_mid_iso2.json b/static/js/mmsi_mid_iso2.json new file mode 100644 index 0000000..b64cd26 --- /dev/null +++ b/static/js/mmsi_mid_iso2.json @@ -0,0 +1 @@ +{"273":"RU","275":"LV","277":"LT","276":"EE","272":"UA","261":"PL","211":"DE","218":"DE","226":"FR","227":"FR","228":"FR","247":"IT","257":"NO","258":"NO","259":"NO","265":"SE","266":"SE","230":"FI","219":"DK","220":"DK","244":"NL","245":"NL","246":"NL","205":"BE","224":"ES","225":"ES","237":"GR","239":"GR","240":"GR","241":"GR","271":"TR","264":"RO","207":"BG","238":"HR","232":"GB","233":"GB","234":"GB","235":"GB","250":"IE","251":"IS","215":"MT","229":"MT","248":"MT","249":"MT","256":"MT","209":"CY","210":"CY","212":"CY","338":"US","366":"US","367":"US","368":"US","369":"US","316":"CA","412":"CN","413":"CN","414":"CN","431":"JP","432":"JP","440":"KR","441":"KR","563":"SG","564":"SG","565":"SG","566":"SG","419":"IN","503":"AU","710":"BR","701":"AR","351":"PA","352":"PA","353":"PA","354":"PA","355":"PA","356":"PA","357":"PA","370":"PA","371":"PA","372":"PA","373":"PA","374":"PA","636":"LR","637":"LR","538":"MH","308":"BS","309":"BS","311":"BS","477":"HK","416":"TW","574":"VN","525":"ID","533":"MY","548":"PH","567":"TH","403":"SA","470":"AE","471":"AE","428":"IL","622":"EG","601":"ZA","512":"NZ","345":"MX","725":"CL","263":"PT","269":"CH","203":"AT","270":"CZ","243":"HU","279":"RS","278":"SI","267":"SK","262":"ME","201":"AL","213":"GE","436":"KZ","422":"IR"} \ No newline at end of file diff --git a/static/js/ship_dims_editor.js b/static/js/ship_dims_editor.js new file mode 100644 index 0000000..a0dc191 --- /dev/null +++ b/static/js/ship_dims_editor.js @@ -0,0 +1,473 @@ +/** + * Редактор габаритов (ITU Fig. 38). + * Корма — L, правый борт — W (доли A/L и C/W фиксируются на время жеста); точка GPS — перетаскивание. + * Для роста L/W координаты могут выходить за контур корпуса (опорный масштаб wPxRef/hPxRef с pointerdown). + */ +(function () { + 'use strict'; + + var NS = 'http://www.w3.org/2000/svg'; + + var MIN_LEN = 20; + var MIN_BEAM = 6; + var MAX_AB = 511; + var MAX_CD = 63; + var MAX_L = MAX_AB + MAX_AB; + var MAX_W = MAX_CD + MAX_CD; + + /** Макс. размер корпуса в пикселях (пропорции сохраняются) */ + var MAX_DRAW_W = 175; + var MAX_DRAW_H = 210; + /** Минимум по меньшей стороне корпуса в px — иначе ручки и клампы «схлопываются» */ + var MIN_HULL_PX = 48; + + var PAD_L = 12; + var PAD_T = 48; + var PAD_R = 78; + var PAD_B = 42; + + var svg, inner, hull, marker, handles; + var gridH, gridV, lblBow, lblStern, dimBeam, dimLength; + var drag = null; + + /** Текущая геометрия (обновляется в refresh) */ + var layout = { + wPx: 100, + hPx: 160, + dispL: MIN_LEN, + dispW: MIN_BEAM, + }; + + function clamp(n, lo, hi) { + return Math.max(lo, Math.min(hi, n)); + } + + function el(name, attrs) { + var e = document.createElementNS(NS, name); + if (attrs) { + Object.keys(attrs).forEach(function (k) { + e.setAttribute(k, attrs[k]); + }); + } + return e; + } + + function readDims() { + var A = parseInt(document.getElementById('tp-to-bow').value, 10) || 0; + var B = parseInt(document.getElementById('tp-to-stern').value, 10) || 0; + var C = parseInt(document.getElementById('tp-to-port').value, 10) || 0; + var D = parseInt(document.getElementById('tp-to-starboard').value, 10) || 0; + A = clamp(A, 0, MAX_AB); + B = clamp(B, 0, MAX_AB); + C = clamp(C, 0, MAX_CD); + D = clamp(D, 0, MAX_CD); + return { A: A, B: B, C: C, D: D, L: A + B, W: C + D }; + } + + function writeDims(A, B, C, D) { + A = clamp(Math.round(A), 0, MAX_AB); + B = clamp(Math.round(B), 0, MAX_AB); + C = clamp(Math.round(C), 0, MAX_CD); + D = clamp(Math.round(D), 0, MAX_CD); + document.getElementById('tp-to-bow').value = A; + document.getElementById('tp-to-stern').value = B; + document.getElementById('tp-to-port').value = C; + document.getElementById('tp-to-starboard').value = D; + } + + function displayLW(d) { + var L = d.L > 0 ? d.L : MIN_LEN; + var W = d.W > 0 ? d.W : MIN_BEAM; + return { L: L, W: W, template: d.L === 0 && d.W === 0 }; + } + + function hullPath(w, h) { + var bow = Math.min(20, h * 0.11); + var tipX = w / 2; + return ( + 'M ' + tipX + ',0 L ' + w + ',' + bow + ' L ' + w + ',' + h + ' L 0,' + h + ' L 0,' + bow + ' Z' + ); + } + + function computeLayout(d, disp) { + var scale = Math.min(MAX_DRAW_W / disp.W, MAX_DRAW_H / disp.L); + if (!isFinite(scale) || scale <= 0) scale = 1; + var wPx0 = disp.W * scale; + var hPx0 = disp.L * scale; + var bump = 1; + if (wPx0 < MIN_HULL_PX) bump = Math.max(bump, MIN_HULL_PX / Math.max(wPx0, 1e-6)); + if (hPx0 < MIN_HULL_PX) bump = Math.max(bump, MIN_HULL_PX / Math.max(hPx0, 1e-6)); + scale *= bump; + var wPx = disp.W * scale; + var hPx = disp.L * scale; + layout.wPx = wPx; + layout.hPx = hPx; + layout.dispL = disp.L; + layout.dispW = disp.W; + layout.scale = scale; + return { sx: scale, sy: scale, wPx: wPx, hPx: hPx }; + } + + function scales(d, disp, geo) { + var wPx = geo.wPx; + var hPx = geo.hPx; + var padX = Math.max(1, Math.min(8, wPx * 0.1)); + var padY = Math.max(1, Math.min(8, hPx * 0.1)); + var aVis = d.L > 0 ? d.A : disp.L / 2; + var cVis = d.W > 0 ? d.C : disp.W / 2; + var mx = clamp(cVis * geo.sx, padX, wPx - padX); + var my = clamp(aVis * geo.sy, padY, hPx - padY); + return { mx: mx, my: my }; + } + + function ensureNonZeroDims() { + var d = readDims(); + if (d.L === 0 && d.W === 0) { + writeDims(MIN_LEN / 2, MIN_LEN / 2, MIN_BEAM / 2, MIN_BEAM / 2); + } + } + + function clearG(g) { + while (g.firstChild) g.removeChild(g.firstChild); + } + + function rebuildBeamDimension(g, wPx, hPx, Wm) { + clearG(g); + var bow = Math.min(20, hPx * 0.11); + var off = 24; + var y = -off; + var yPast = y - 1.5; + g.appendChild( + el('line', { class: 'ship-dim-ext', x1: 0, y1: bow, x2: 0, y2: yPast }) + ); + g.appendChild( + el('line', { class: 'ship-dim-ext', x1: wPx, y1: bow, x2: wPx, y2: yPast }) + ); + g.appendChild( + el('line', { + class: 'ship-dim-main', + x1: 0, + y1: y, + x2: wPx, + y2: y, + 'marker-start': 'url(#ship-dim-arrow)', + 'marker-end': 'url(#ship-dim-arrow)', + }) + ); + var t = el('text', { + class: 'ship-dim-txt', + x: wPx / 2, + y: y - 8, + 'text-anchor': 'middle', + }); + t.textContent = 'W = ' + Wm + ' м'; + g.appendChild(t); + } + + function rebuildLengthDimension(g, wPx, hPx, Lm) { + clearG(g); + var bow = Math.min(20, hPx * 0.11); + var off = 22; + var x = wPx + off; + var xPast = x + 1.5; + g.appendChild( + el('line', { class: 'ship-dim-ext', x1: wPx, y1: bow, x2: x, y2: bow }) + ); + g.appendChild( + el('line', { class: 'ship-dim-ext', x1: x, y1: bow, x2: x, y2: 0 }) + ); + g.appendChild( + el('line', { class: 'ship-dim-ext', x1: wPx, y1: hPx, x2: xPast, y2: hPx }) + ); + g.appendChild( + el('line', { + class: 'ship-dim-main', + x1: x, + y1: 0, + x2: x, + y2: hPx, + 'marker-start': 'url(#ship-dim-arrow)', + 'marker-end': 'url(#ship-dim-arrow)', + }) + ); + var t = el('text', { + class: 'ship-dim-txt', + x: x + 14, + y: hPx / 2, + 'text-anchor': 'middle', + transform: 'rotate(-90 ' + (x + 14) + ' ' + hPx / 2 + ')', + }); + t.textContent = 'L = ' + Lm + ' м'; + g.appendChild(t); + } + + function positionHandles(wPx, hPx) { + if (!handles) return; + var stern = handles.querySelector('[data-ship-edge="stern"]'); + var sb = handles.querySelector('[data-ship-edge="starboard"]'); + if (stern) stern.setAttribute('transform', 'translate(' + wPx / 2 + ',' + hPx + ')'); + if (sb) sb.setAttribute('transform', 'translate(' + wPx + ',' + hPx / 2 + ')'); + } + + function refreshFromInputs() { + if (!svg || !inner || !hull || !marker) return; + var d = readDims(); + var disp = displayLW(d); + var geo = computeLayout(d, disp); + var sc = scales(d, disp, geo); + var wPx = geo.wPx; + var hPx = geo.hPx; + + inner.setAttribute('transform', 'translate(' + PAD_L + ',' + PAD_T + ')'); + + var vbW = PAD_L + wPx + PAD_R; + var vbH = PAD_T + hPx + PAD_B; + svg.setAttribute('viewBox', '0 0 ' + vbW + ' ' + vbH); + + rebuildBeamDimension(dimBeam, wPx, hPx, disp.W); + rebuildLengthDimension(dimLength, wPx, hPx, disp.L); + + hull.setAttribute('d', hullPath(wPx, hPx)); + + if (gridH && gridV) { + gridH.setAttribute('x1', 0); + gridH.setAttribute('y1', sc.my); + gridH.setAttribute('x2', wPx); + gridH.setAttribute('y2', sc.my); + gridV.setAttribute('x1', sc.mx); + gridV.setAttribute('y1', 0); + gridV.setAttribute('x2', sc.mx); + gridV.setAttribute('y2', hPx); + } + + if (lblBow) { + lblBow.setAttribute('x', wPx / 2); + lblBow.setAttribute('y', -38); + } + if (lblStern) { + lblStern.setAttribute('x', wPx / 2); + lblStern.setAttribute('y', hPx + 22); + } + + marker.setAttribute('transform', 'translate(' + sc.mx + ',' + sc.my + ')'); + marker.setAttribute('class', 'ship-gps-group' + (disp.template ? ' ship-gps-group--template' : '')); + + positionHandles(wPx, hPx); + } + + function svgPointFromClient(clientX, clientY) { + var pt = svg.createSVGPoint(); + pt.x = clientX; + pt.y = clientY; + var ctm = inner.getScreenCTM(); + if (!ctm) return null; + var p = pt.matrixTransform(ctm.inverse()); + return { x: p.x, y: p.y }; + } + + function splitLength(u, Ls) { + u = clamp(u, 0, 1); + var A = Math.round(u * Ls); + A = clamp(A, 0, MAX_AB); + var B = Ls - A; + if (B > MAX_AB) { + B = MAX_AB; + A = clamp(Ls - B, 0, MAX_AB); + } + if (B < 0) { + B = 0; + A = clamp(Ls, 0, MAX_AB); + } + return { A: A, B: B }; + } + + function splitBeam(v, Ws) { + v = clamp(v, 0, 1); + var C = Math.round(v * Ws); + C = clamp(C, 0, MAX_CD); + var D = Ws - C; + if (D > MAX_CD) { + D = MAX_CD; + C = clamp(Ws - D, 0, MAX_CD); + } + if (D < 0) { + D = 0; + C = clamp(Ws, 0, MAX_CD); + } + return { C: C, D: D }; + } + + /** Новая длина L; сохраняем долю носа A/L ≈ rA. */ + function abFromLength(Ln, rA) { + Ln = clamp(Math.round(Ln), 1, MAX_L); + var r = clamp(isFinite(rA) ? rA : 0.5, 0, 1); + var A = Math.round(r * Ln); + A = clamp(A, 0, MAX_AB); + var B = Ln - A; + if (B > MAX_AB) { + B = MAX_AB; + A = clamp(Ln - B, 0, MAX_AB); + } + if (B < 0) { + B = 0; + A = clamp(Ln, 0, MAX_AB); + } + return { A: A, B: B }; + } + + /** Новая ширина W; сохраняем долю порта C/W ≈ rC. */ + function cdFromWidth(Wn, rC) { + Wn = clamp(Math.round(Wn), 1, MAX_W); + var r = clamp(isFinite(rC) ? rC : 0.5, 0, 1); + var C = Math.round(r * Wn); + C = clamp(C, 0, MAX_CD); + var D = Wn - C; + if (D > MAX_CD) { + D = MAX_CD; + C = clamp(Wn - D, 0, MAX_CD); + } + if (D < 0) { + D = 0; + C = clamp(Wn, 0, MAX_CD); + } + return { C: C, D: D }; + } + + function startDrag(ev, kind, captureEl) { + ev.preventDefault(); + captureEl.setPointerCapture(ev.pointerId); + document.body.classList.add('ship-editor-dragging'); + document.addEventListener('pointermove', onDocumentPointerMove); + document.addEventListener('pointerup', onDocumentPointerEnd); + document.addEventListener('pointercancel', onDocumentPointerEnd); + return { kind: kind, pid: ev.pointerId, el: captureEl }; + } + + function stopDragListeners() { + document.removeEventListener('pointermove', onDocumentPointerMove); + document.removeEventListener('pointerup', onDocumentPointerEnd); + document.removeEventListener('pointercancel', onDocumentPointerEnd); + } + + function onInnerPointerDown(ev) { + if (!inner) return; + var edgeG = ev.target.closest ? ev.target.closest('[data-ship-edge]') : null; + if (edgeG) { + ensureNonZeroDims(); + var d0 = readDims(); + var disp0 = displayLW(d0); + var geo0 = computeLayout(d0, disp0); + var kind = edgeG.getAttribute('data-ship-edge'); + drag = startDrag(ev, kind, edgeG); + drag.L0 = d0.L; + drag.W0 = d0.W; + drag.rA = d0.L > 0 ? d0.A / d0.L : 0.5; + drag.rC = d0.W > 0 ? d0.C / d0.W : 0.5; + drag.wPxRef = geo0.wPx; + drag.hPxRef = geo0.hPx; + return; + } + if (marker && marker.contains(ev.target)) { + ensureNonZeroDims(); + var dg = readDims(); + var Ls = dg.L > 0 ? Math.min(dg.L, MAX_L) : MIN_LEN; + var Ws = dg.W > 0 ? Math.min(dg.W, MAX_W) : MIN_BEAM; + Ls = clamp(Ls, 1, MAX_L); + Ws = clamp(Ws, 1, MAX_W); + drag = startDrag(ev, 'gps', marker); + drag.Ls = Ls; + drag.Ws = Ws; + } + } + + function onDocumentPointerMove(ev) { + if (!drag || ev.pointerId !== drag.pid) return; + var p = svgPointFromClient(ev.clientX, ev.clientY); + if (!p) return; + + var d = readDims(); + var disp = displayLW(d); + var geo = computeLayout(d, disp); + var wPx = geo.wPx; + var hPx = geo.hPx; + + if (drag.kind === 'gps') { + var u = p.y / hPx; + var v = p.x / wPx; + var ab = splitLength(u, drag.Ls); + var cd = splitBeam(v, drag.Ws); + writeDims(ab.A, ab.B, cd.C, cd.D); + refreshFromInputs(); + return; + } + + var wRef = drag.wPxRef; + var hRef = drag.hPxRef; + if (drag.kind === 'stern' && wRef > 0 && hRef > 0) { + var yMin = (hRef * 1) / Math.max(drag.L0, 1); + var yMax = (hRef * MAX_L) / Math.max(drag.L0, 1); + var yS = clamp(p.y, yMin, yMax); + var L1 = Math.round((drag.L0 * yS) / hRef); + L1 = clamp(L1, 1, MAX_L); + var abS = abFromLength(L1, drag.rA); + writeDims(abS.A, abS.B, d.C, d.D); + } else if (drag.kind === 'starboard' && wRef > 0 && hRef > 0) { + var xMin = (wRef * 1) / Math.max(drag.W0, 1); + var xMax = (wRef * MAX_W) / Math.max(drag.W0, 1); + var xSb = clamp(p.x, xMin, xMax); + var W1s = Math.round((drag.W0 * xSb) / wRef); + W1s = clamp(W1s, 1, MAX_W); + var cd2 = cdFromWidth(W1s, drag.rC); + writeDims(d.A, d.B, cd2.C, cd2.D); + } + + refreshFromInputs(); + } + + function onDocumentPointerEnd(ev) { + if (!drag || ev.pointerId !== drag.pid) return; + stopDragListeners(); + try { + drag.el.releasePointerCapture(ev.pointerId); + } catch (e) { /* ignore */ } + drag = null; + document.body.classList.remove('ship-editor-dragging'); + } + + function bindInputs() { + ['tp-to-bow', 'tp-to-stern', 'tp-to-port', 'tp-to-starboard'].forEach(function (id) { + var eln = document.getElementById(id); + if (eln) { + eln.addEventListener('input', refreshFromInputs); + eln.addEventListener('change', refreshFromInputs); + } + }); + } + + function boot() { + svg = document.getElementById('ship-editor-svg'); + inner = document.getElementById('ship-editor-inner'); + hull = document.getElementById('ship-hull'); + marker = document.getElementById('ship-gps-marker'); + handles = document.getElementById('ship-edge-handles'); + gridH = document.getElementById('ship-grid-h'); + gridV = document.getElementById('ship-grid-v'); + lblBow = document.getElementById('ship-lbl-bow'); + lblStern = document.getElementById('ship-lbl-stern'); + dimBeam = document.getElementById('ship-dim-beam'); + dimLength = document.getElementById('ship-dim-length'); + if (!svg || !inner || !hull || !marker) return; + + inner.addEventListener('pointerdown', onInnerPointerDown); + bindInputs(); + refreshFromInputs(); + } + + window.shipDimsEditorRefresh = refreshFromInputs; + + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', boot); + } else { + boot(); + } +})(); diff --git a/static/js/ship_types_table51.js b/static/js/ship_types_table51.js new file mode 100644 index 0000000..3628115 --- /dev/null +++ b/static/js/ship_types_table51.js @@ -0,0 +1,179 @@ +/** + * ITU-R M.1371-6, табл. 51 — тип судна (идентификатор 0–99). + * Подписи на русском по официальным формулировкам (сокращ.: ОПГ — опасные грузы; + * ОН — опасные только насыпью; ВВ — вредные вещества; МЗ — морские загрязнители). + */ +(function () { + 'use strict'; + + var LABELS = [ + 'Не указано', + 'Научно-исследовательское судно', + 'Учебное судно', + 'Судно, принадлежащее или эксплуатируемое государственным органом', + 'Ледокол', + 'Судно обслуживания буёв (навигационных знаков)', + 'Кабелеукладчик', + 'Трубоукладчик', + 'Зарезервировано (на будущее)', + 'Судно специального назначения, дополнительные сведения не указаны', + 'Зарезервировано (на будущее)', + 'Судно FPSO (добыча, хранение и отгрузка нефти на месторождении)', + 'Рыбозавод (плавучий рыбоперерабатывающий завод)', + 'Судно обеспечения рыбоводческого хозяйства', + 'Судно оффшорного обеспечения и т. п.', + 'Зарезервировано (на будущее)', + 'Зарезервировано (на будущее)', + 'Судно для строительных работ', + 'Катер доставки экипажа (crew boat)', + 'Судно обеспечения, дополнительные сведения не указаны', + 'Судно на воздушной подушке (WIG), все суда этого типа', + 'WIG с ОПГ и/или ОН, ВВ или МЗ, категория опасности X (ИМО)', + 'WIG с ОПГ и/или ОН, ВВ или МЗ, категория опасности Y (ИМО)', + 'WIG с ОПГ и/или ОН, ВВ или МЗ, категория опасности Z (ИМО)', + 'WIG с ОПГ и/или ОН, ВВ или МЗ, категория OS (ИМО)', + 'Зарезервировано (на будущее)', + 'Зарезервировано (на будущее)', + 'Зарезервировано (на будущее)', + 'Зарезервировано (на будущее)', + 'WIG, дополнительные сведения не указаны', + 'Рыболовное судно', + 'Буксир', + 'Буксир; длина буксира >200 м или ширина >25 м', + 'Земснаряд', + 'Водолазное судно', + 'Военный корабль или вспомогательное судно ВМС', + 'Парусное судно', + 'Прогулочное моторное судно', + 'Тральщик', + 'Патрульное судно', + 'Высокоскоростное судно (ВСС), все суда этого типа', + 'ВСС с ОПГ и/или ОН, ВВ или МЗ, категория X (ИМО)', + 'ВСС с ОПГ и/или ОН, ВВ или МЗ, категория Y (ИМО)', + 'ВСС с ОПГ и/или ОН, ВВ или МЗ, категория Z (ИМО)', + 'ВСС с ОПГ и/или ОН, ВВ или МЗ, категория OS (ИМО)', + 'ВСС, перевозка пассажиров', + 'ВСС Ro-Ro (автомобили / ж/д)', + 'Зарезервировано (на будущее)', + 'Зарезервировано (на будущее)', + 'ВСС, дополнительные сведения не указаны', + 'Лоцманское судно', + 'Поисково-спасательное судно', + 'Буксиры', + 'Портовое или рыболовное вспомогательное судно', + 'Противозагрязнение или пожарный резерв', + 'Судно правоохранительных органов', + 'Резерв 1 — для назначений местным судам', + 'Резерв 2 — для назначений местным судам', + 'Медицинский транспорт (Женевские конвенции 1949 г. и Доп. протоколы)', + 'Судна государств, не участвующих в вооружённом конфликте', + 'Пассажирское судно, все суда этого типа', + 'Пассажирское с ОПГ и/или ОН, ВВ или МЗ, категория X (ИМО)', + 'Пассажирское с ОПГ и/или ОН, ВВ или МЗ, категория Y (ИМО)', + 'Пассажирское с ОПГ и/или ОН, ВВ или МЗ, категория Z (ИМО)', + 'Пассажирское с ОПГ и/или ОН, ВВ или МЗ, категория OS (ИМО)', + 'Пассажирский лайнер (круизное судно)', + 'Пассажирский паром', + 'Пассажирское прогулочное (напр. по гавани, наблюдение за китами)', + 'Зарезервировано (на будущее)', + 'Пассажирское судно, дополнительные сведения не указаны', + 'Грузовое судно, все суда этого типа', + 'Грузовое с ОПГ и/или ОН, ВВ или МЗ, категория X (ИМО)', + 'Грузовое с ОПГ и/или ОН, ВВ или МЗ, категория Y (ИМО)', + 'Грузовое с ОПГ и/или ОН, ВВ или МЗ, категория Z (ИМО)', + 'Грузовое с ОПГ и/или ОН, ВВ или МЗ, категория OS (ИМО)', + 'Грузовое судно, балкер', + 'Грузовое судно, контейнеровоз', + 'Грузовое судно, Ro-Ro', + 'Грузовое судно, десантная баржа', + 'Грузовое судно, дополнительные сведения не указаны', + 'Танкер, все суда этого типа', + 'Танкер с ОПГ и/или ОН, ВВ или МЗ, категория X (ИМО)', + 'Танкер с ОПГ и/или ОН, ВВ или МЗ, категория Y (ИМО)', + 'Танкер с ОПГ и/или ОН, ВВ или МЗ, категория Z (ИМО)', + 'Танкер с ОПГ и/или ОН, ВВ или МЗ, категория OS (ИМО)', + 'Танкер, неопасный груз / не загрязняющий', + 'Составной буксир и танкерная баржа (A–D — буксир и баржа)', + 'Зарезервировано (на будущее)', + 'Зарезервировано (на будущее)', + 'Танкер, дополнительные сведения не указаны', + 'Прочий тип судна', + 'Прочий тип с ОПГ и/или ОН, ВВ или МЗ, категория X (ИМО)', + 'Прочий тип с ОПГ и/или ОН, ВВ или МЗ, категория Y (ИМО)', + 'Прочий тип с ОПГ и/или ОН, ВВ или МЗ, категория Z (ИМО)', + 'Прочий тип с ОПГ и/или ОН, ВВ или МЗ, категория OS (ИМО)', + 'Зарезервировано (на будущее)', + 'Зарезервировано (на будущее)', + 'Зарезервировано (на будущее)', + 'Зарезервировано (на будущее)', + 'Прочий тип судна, дополнительные сведения не указаны', + ]; + + var GROUPS = [ + { from: 1, to: 9, title: '01–09 Судно специального назначения' }, + { from: 10, to: 19, title: '10–19 Судно обеспечения' }, + { from: 20, to: 29, title: '20–29 Судно на воздушной подушке (WIG)' }, + { from: 30, to: 39, title: '30–39 Особое судно' }, + { from: 40, to: 49, title: '40–49 Высокоскоростное судно (ВСС)' }, + { from: 50, to: 59, title: '50–59 Особое судно' }, + { from: 60, to: 69, title: '60–69 Пассажирское судно' }, + { from: 70, to: 79, title: '70–79 Грузовое судно' }, + { from: 80, to: 89, title: '80–89 Танкер' }, + { from: 90, to: 99, title: '90–99 Прочие типы' }, + ]; + + function addOption(sel, value, text) { + var o = document.createElement('option'); + o.value = String(value); + o.textContent = String(value) + ' — ' + text; + sel.appendChild(o); + } + + function fillShipTypeSelect() { + var sel = document.getElementById('tp-ship-type'); + if (!sel || sel.getAttribute('data-table51') === '1') return; + sel.setAttribute('data-table51', '1'); + sel.innerHTML = ''; + addOption(sel, 0, LABELS[0]); + GROUPS.forEach(function (g) { + var og = document.createElement('optgroup'); + og.label = g.title; + for (var c = g.from; c <= g.to; c++) { + var o = document.createElement('option'); + o.value = String(c); + var pad = c < 10 ? '0' + c : String(c); + o.textContent = pad + ' — ' + LABELS[c]; + og.appendChild(o); + } + sel.appendChild(og); + }); + } + + function ensureShipTypeOption(value) { + var sel = document.getElementById('tp-ship-type'); + if (!sel) return; + var v = Math.max(0, Math.min(255, parseInt(value, 10) || 0)); + var s = String(v); + for (var i = 0; i < sel.options.length; i++) { + if (sel.options[i].value === s) return; + } + var o = document.createElement('option'); + o.value = s; + o.textContent = s + ' — (из конфигурации, вне табл. 51 / 0–99)'; + sel.appendChild(o); + } + + window.fillShipTypeSelect = fillShipTypeSelect; + window.ensureShipTypeOption = ensureShipTypeOption; + window.AIS_TABLE51_SHIP_TYPE_LABEL = function (code) { + var c = parseInt(code, 10); + if (isNaN(c) || c < 0 || c > 99) return null; + return LABELS[c] || null; + }; + + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', fillShipTypeSelect); + } else { + fillShipTypeSelect(); + } +})(); diff --git a/static/leaflet/Leaflet.VectorGrid.bundled.min.js b/static/leaflet/Leaflet.VectorGrid.bundled.min.js new file mode 100644 index 0000000..1640f3d --- /dev/null +++ b/static/leaflet/Leaflet.VectorGrid.bundled.min.js @@ -0,0 +1,2 @@ +"use strict";function __$strToBlobUri(t,e,r){try{return window.URL.createObjectURL(new Blob([Uint8Array.from(t.split("").map(function(t){return t.charCodeAt(0)}))],{type:e}))}catch(i){return"data:"+e+(r?";base64,":",")+t}}function Pbf(t){this.buf=ArrayBuffer.isView&&ArrayBuffer.isView(t)?t:new Uint8Array(t||0),this.pos=0,this.type=0,this.length=this.buf.length}function readVarintRemainder(t,e,r){var i,n,o=r.buf;if(n=o[r.pos++],i=(112&n)>>4,n<128)return toNum(t,i,e);if(n=o[r.pos++],i|=(127&n)<<3,n<128)return toNum(t,i,e);if(n=o[r.pos++],i|=(127&n)<<10,n<128)return toNum(t,i,e);if(n=o[r.pos++],i|=(127&n)<<17,n<128)return toNum(t,i,e);if(n=o[r.pos++],i|=(127&n)<<24,n<128)return toNum(t,i,e);if(n=o[r.pos++],i|=(1&n)<<31,n<128)return toNum(t,i,e);throw new Error("Expected varint not more than 10 bytes")}function readPackedEnd(t){return t.type===Pbf.Bytes?t.readVarint()+t.pos:t.pos+1}function toNum(t,e,r){return r?4294967296*e+(t>>>0):4294967296*(e>>>0)+(t>>>0)}function writeBigVarint(t,e){var r,i;if(t>=0?(r=t%4294967296|0,i=t/4294967296|0):(r=~(-t%4294967296),i=~(-t/4294967296),4294967295^r?r=r+1|0:(r=0,i=i+1|0)),t>=0x10000000000000000||t<-0x10000000000000000)throw new Error("Given varint doesn't fit into 10 bytes");e.realloc(10),writeBigVarintLow(r,i,e),writeBigVarintHigh(i,e)}function writeBigVarintLow(t,e,r){r.buf[r.pos++]=127&t|128,t>>>=7,r.buf[r.pos++]=127&t|128,t>>>=7,r.buf[r.pos++]=127&t|128,t>>>=7,r.buf[r.pos++]=127&t|128,t>>>=7,r.buf[r.pos]=127&t}function writeBigVarintHigh(t,e){var r=(7&t)<<4;e.buf[e.pos++]|=r|((t>>>=3)?128:0),t&&(e.buf[e.pos++]=127&t|((t>>>=7)?128:0),t&&(e.buf[e.pos++]=127&t|((t>>>=7)?128:0),t&&(e.buf[e.pos++]=127&t|((t>>>=7)?128:0),t&&(e.buf[e.pos++]=127&t|((t>>>=7)?128:0),t&&(e.buf[e.pos++]=127&t)))))}function makeRoomForExtraLength(t,e,r){var i=e<=16383?1:e<=2097151?2:e<=268435455?3:Math.ceil(Math.log(e)/(7*Math.LN2));r.realloc(i);for(var n=r.pos-1;n>=t;n--)r.buf[n+i]=r.buf[n]}function writePackedVarint(t,e){for(var r=0;r>>8,t[r+2]=e>>>16,t[r+3]=e>>>24}function readInt32(t,e){return(t[e]|t[e+1]<<8|t[e+2]<<16)+(t[e+3]<<24)}function readUtf8(t,e,r){for(var i="",n=e;n239?4:o>223?3:o>191?2:1;if(n+a>r)break;var u,h,l;1===a?o<128&&(s=o):2===a?128==(192&(u=t[n+1]))&&(s=(31&o)<<6|63&u)<=127&&(s=null):3===a?(u=t[n+1],h=t[n+2],128==(192&u)&&128==(192&h)&&((s=(15&o)<<12|(63&u)<<6|63&h)<=2047||s>=55296&&s<=57343)&&(s=null)):4===a&&(u=t[n+1],h=t[n+2],l=t[n+3],128==(192&u)&&128==(192&h)&&128==(192&l)&&((s=(15&o)<<18|(63&u)<<12|(63&h)<<6|63&l)<=65535||s>=1114112)&&(s=null)),null===s?(s=65533,a=1):s>65535&&(s-=65536,i+=String.fromCharCode(s>>>10&1023|55296),s=56320|1023&s),i+=String.fromCharCode(s),n+=a}return i}function writeUtf8(t,e,r){for(var i,n,o=0;o55295&&i<57344){if(!n){i>56319||o+1===e.length?(t[r++]=239,t[r++]=191,t[r++]=189):n=i;continue}if(i<56320){t[r++]=239,t[r++]=191,t[r++]=189,n=i;continue}i=n-55296<<10|i-56320|65536,n=null}else n&&(t[r++]=239,t[r++]=191,t[r++]=189,n=null);i<128?t[r++]=i:(i<2048?t[r++]=i>>6|192:(i<65536?t[r++]=i>>12|224:(t[r++]=i>>18|240,t[r++]=i>>12&63|128),t[r++]=i>>6&63|128),t[r++]=63&i|128)}return r}function Point$1(t,e){this.x=t,this.y=e}function VectorTileFeature$2(t,e,r,i,n){this.properties={},this.extent=r,this.type=0,this._pbf=t,this._geometry=-1,this._keys=i,this._values=n,t.readFields(readFeature,this,e)}function readFeature(t,e,r){1==t?e.id=r.readVarint():2==t?readTag(r,e):3==t?e.type=r.readVarint():4==t&&(e._geometry=r.pos)}function readTag(t,e){for(var r=t.readVarint()+t.pos;t.pos>3;e=1===i?t.readString():2===i?t.readFloat():3===i?t.readDouble():4===i?t.readVarint64():5===i?t.readVarint():6===i?t.readSVarint():7===i?t.readBoolean():null}return e}function VectorTile$1(t,e){this.layers=t.readFields(readTile,{},e)}function readTile(t,e,r){if(3===t){var i=new VectorTileLayer$1(r,r.readVarint()+r.pos);i.length&&(e[i.name]=i)}}!function(t){function e(t){if("string"!=typeof t&&(t=String(t)),/[^a-z0-9\-#$%&'*+.\^_`|~]/i.test(t))throw new TypeError("Invalid character in header field name");return t.toLowerCase()}function r(t){return"string"!=typeof t&&(t=String(t)),t}function i(t){var e={next:function(){var e=t.shift();return{done:void 0===e,value:e}}};return v.iterable&&(e[Symbol.iterator]=function(){return e}),e}function n(t){this.map={},t instanceof n?t.forEach(function(t,e){this.append(e,t)},this):Array.isArray(t)?t.forEach(function(t){this.append(t[0],t[1])},this):t&&Object.getOwnPropertyNames(t).forEach(function(e){this.append(e,t[e])},this)}function o(t){if(t.bodyUsed)return Promise.reject(new TypeError("Already read"));t.bodyUsed=!0}function s(t){return new Promise(function(e,r){t.onload=function(){e(t.result)},t.onerror=function(){r(t.error)}})}function a(t){var e=new FileReader,r=s(e);return e.readAsArrayBuffer(t),r}function u(t){var e=new FileReader,r=s(e);return e.readAsText(t),r}function h(t){for(var e=new Uint8Array(t),r=new Array(e.length),i=0;i-1?e:t}function p(t,e){e=e||{};var r=e.body;if(t instanceof p){if(t.bodyUsed)throw new TypeError("Already read");this.url=t.url,this.credentials=t.credentials,e.headers||(this.headers=new n(t.headers)),this.method=t.method,this.mode=t.mode,r||null==t._bodyInit||(r=t._bodyInit,t.bodyUsed=!0)}else this.url=String(t);if(this.credentials=e.credentials||this.credentials||"omit",!e.headers&&this.headers||(this.headers=new n(e.headers)),this.method=f(e.method||this.method||"GET"),this.mode=e.mode||this.mode||null,this.referrer=null,("GET"===this.method||"HEAD"===this.method)&&r)throw new TypeError("Body not allowed for GET or HEAD requests");this._initBody(r)}function d(t){var e=new FormData;return t.trim().split("&").forEach(function(t){if(t){var r=t.split("="),i=r.shift().replace(/\+/g," "),n=r.join("=").replace(/\+/g," ");e.append(decodeURIComponent(i),decodeURIComponent(n))}}),e}function y(t){var e=new n;return t.split(/\r?\n/).forEach(function(t){var r=t.split(":"),i=r.shift().trim();if(i){var n=r.join(":").trim();e.append(i,n)}}),e}function m(t,e){e||(e={}),this.type="default",this.status="status"in e?e.status:200,this.ok=this.status>=200&&this.status<300,this.statusText="statusText"in e?e.statusText:"OK",this.headers=new n(e.headers),this.url=e.url||"",this._initBody(t)}if(!t.fetch){var v={searchParams:"URLSearchParams"in t,iterable:"Symbol"in t&&"iterator"in Symbol,blob:"FileReader"in t&&"Blob"in t&&function(){try{return new Blob,!0}catch(t){return!1}}(),formData:"FormData"in t,arrayBuffer:"ArrayBuffer"in t};if(v.arrayBuffer)var g=["[object Int8Array]","[object Uint8Array]","[object Uint8ClampedArray]","[object Int16Array]","[object Uint16Array]","[object Int32Array]","[object Uint32Array]","[object Float32Array]","[object Float64Array]"],b=function(t){return t&&DataView.prototype.isPrototypeOf(t)},w=ArrayBuffer.isView||function(t){return t&&g.indexOf(Object.prototype.toString.call(t))>-1};n.prototype.append=function(t,i){t=e(t),i=r(i);var n=this.map[t];this.map[t]=n?n+","+i:i},n.prototype.delete=function(t){delete this.map[e(t)]},n.prototype.get=function(t){return t=e(t),this.has(t)?this.map[t]:null},n.prototype.has=function(t){return this.map.hasOwnProperty(e(t))},n.prototype.set=function(t,i){this.map[e(t)]=r(i)},n.prototype.forEach=function(t,e){var r=this;for(var i in this.map)r.map.hasOwnProperty(i)&&t.call(e,r.map[i],i,r)},n.prototype.keys=function(){var t=[];return this.forEach(function(e,r){t.push(r)}),i(t)},n.prototype.values=function(){var t=[];return this.forEach(function(e){t.push(e)}),i(t)},n.prototype.entries=function(){var t=[];return this.forEach(function(e,r){t.push([r,e])}),i(t)},v.iterable&&(n.prototype[Symbol.iterator]=n.prototype.entries);var _=["DELETE","GET","HEAD","OPTIONS","POST","PUT"];p.prototype.clone=function(){return new p(this,{body:this._bodyInit})},c.call(p.prototype),c.call(m.prototype),m.prototype.clone=function(){return new m(this._bodyInit,{status:this.status,statusText:this.statusText,headers:new n(this.headers),url:this.url})},m.error=function(){var t=new m(null,{status:0,statusText:""});return t.type="error",t};var x=[301,302,303,307,308];m.redirect=function(t,e){if(-1===x.indexOf(e))throw new RangeError("Invalid status code");return new m(null,{status:e,headers:{location:t}})},t.Headers=n,t.Request=p,t.Response=m,t.fetch=function(t,e){return new Promise(function(r,i){var n=new p(t,e),o=new XMLHttpRequest;o.onload=function(){var t={status:o.status,statusText:o.statusText,headers:y(o.getAllResponseHeaders()||"")};t.url="responseURL"in o?o.responseURL:t.headers.get("X-Request-URL");var e="response"in o?o.response:o.responseText;r(new m(e,t))},o.onerror=function(){i(new TypeError("Network request failed"))},o.ontimeout=function(){i(new TypeError("Network request failed"))},o.open(n.method,n.url,!0),"include"===n.credentials&&(o.withCredentials=!0),"responseType"in o&&v.blob&&(o.responseType="blob"),n.headers.forEach(function(t,e){o.setRequestHeader(e,t)}),o.send(void 0===n._bodyInit?null:n._bodyInit)})},t.fetch.polyfill=!0}}("undefined"!=typeof self?self:void 0);var read=function(t,e,r,i,n){var o,s,a=8*n-i-1,u=(1<>1,l=-7,c=r?n-1:0,f=r?-1:1,p=t[e+c];for(c+=f,o=p&(1<<-l)-1,p>>=-l,l+=a;l>0;o=256*o+t[e+c],c+=f,l-=8);for(s=o&(1<<-l)-1,o>>=-l,l+=i;l>0;s=256*s+t[e+c],c+=f,l-=8);if(0===o)o=1-h;else{if(o===u)return s?NaN:1/0*(p?-1:1);s+=Math.pow(2,i),o-=h}return(p?-1:1)*s*Math.pow(2,o-i)},write=function(t,e,r,i,n,o){var s,a,u,h=8*o-n-1,l=(1<>1,f=23===n?Math.pow(2,-24)-Math.pow(2,-77):0,p=i?0:o-1,d=i?1:-1,y=e<0||0===e&&1/e<0?1:0;for(e=Math.abs(e),isNaN(e)||e===1/0?(a=isNaN(e)?1:0,s=l):(s=Math.floor(Math.log(e)/Math.LN2),e*(u=Math.pow(2,-s))<1&&(s--,u*=2),e+=s+c>=1?f/u:f*Math.pow(2,1-c),e*u>=2&&(s++,u/=2),s+c>=l?(a=0,s=l):s+c>=1?(a=(e*u-1)*Math.pow(2,n),s+=c):(a=e*Math.pow(2,c-1)*Math.pow(2,n),s=0));n>=8;t[r+p]=255&a,p+=d,a/=256,n-=8);for(s=s<0;t[r+p]=255&s,p+=d,s/=256,h-=8);t[r+p-d]|=128*y},index$1={read:read,write:write},index=Pbf,ieee754=index$1;Pbf.Varint=0,Pbf.Fixed64=1,Pbf.Bytes=2,Pbf.Fixed32=5;var SHIFT_LEFT_32=4294967296,SHIFT_RIGHT_32=1/SHIFT_LEFT_32;Pbf.prototype={destroy:function(){this.buf=null},readFields:function(t,e,r){var i=this;for(r=r||this.length;this.pos>3,s=i.pos;i.type=7&n,t(o,e,i),i.pos===s&&i.skip(n)}return e},readMessage:function(t,e){return this.readFields(t,e,this.readVarint()+this.pos)},readFixed32:function(){var t=readUInt32(this.buf,this.pos);return this.pos+=4,t},readSFixed32:function(){var t=readInt32(this.buf,this.pos);return this.pos+=4,t},readFixed64:function(){var t=readUInt32(this.buf,this.pos)+readUInt32(this.buf,this.pos+4)*SHIFT_LEFT_32;return this.pos+=8,t},readSFixed64:function(){var t=readUInt32(this.buf,this.pos)+readInt32(this.buf,this.pos+4)*SHIFT_LEFT_32;return this.pos+=8,t},readFloat:function(){var t=ieee754.read(this.buf,this.pos,!0,23,4);return this.pos+=4,t},readDouble:function(){var t=ieee754.read(this.buf,this.pos,!0,52,8);return this.pos+=8,t},readVarint:function(t){var e,r,i=this.buf;return r=i[this.pos++],e=127&r,r<128?e:(r=i[this.pos++],e|=(127&r)<<7,r<128?e:(r=i[this.pos++],e|=(127&r)<<14,r<128?e:(r=i[this.pos++],e|=(127&r)<<21,r<128?e:(r=i[this.pos],e|=(15&r)<<28,readVarintRemainder(e,t,this)))))},readVarint64:function(){return this.readVarint(!0)},readSVarint:function(){var t=this.readVarint();return t%2==1?(t+1)/-2:t/2},readBoolean:function(){return Boolean(this.readVarint())},readString:function(){var t=this.readVarint()+this.pos,e=readUtf8(this.buf,this.pos,t);return this.pos=t,e},readBytes:function(){var t=this.readVarint()+this.pos,e=this.buf.subarray(this.pos,t);return this.pos=t,e},readPackedVarint:function(t,e){var r=this,i=readPackedEnd(this);for(t=t||[];this.pos127;);else if(e===Pbf.Bytes)this.pos=this.readVarint()+this.pos;else if(e===Pbf.Fixed32)this.pos+=4;else{if(e!==Pbf.Fixed64)throw new Error("Unimplemented type: "+e);this.pos+=8}},writeTag:function(t,e){this.writeVarint(t<<3|e)},realloc:function(t){for(var e=this.length||16;e268435455||t<0)return void writeBigVarint(t,this);this.realloc(4),this.buf[this.pos++]=127&t|(t>127?128:0),t<=127||(this.buf[this.pos++]=127&(t>>>=7)|(t>127?128:0),t<=127||(this.buf[this.pos++]=127&(t>>>=7)|(t>127?128:0),t<=127||(this.buf[this.pos++]=t>>>7&127)))},writeSVarint:function(t){this.writeVarint(t<0?2*-t-1:2*t)},writeBoolean:function(t){this.writeVarint(Boolean(t))},writeString:function(t){t=String(t),this.realloc(4*t.length),this.pos++;var e=this.pos;this.pos=writeUtf8(this.buf,t,this.pos);var r=this.pos-e;r>=128&&makeRoomForExtraLength(e,r,this),this.pos=e-1,this.writeVarint(r),this.pos+=r},writeFloat:function(t){this.realloc(4),ieee754.write(this.buf,t,this.pos,!0,23,4),this.pos+=4},writeDouble:function(t){this.realloc(8),ieee754.write(this.buf,t,this.pos,!0,52,8),this.pos+=8},writeBytes:function(t){var e=this,r=t.length;this.writeVarint(r),this.realloc(r);for(var i=0;i=128&&makeRoomForExtraLength(r,i,this),this.pos=r-1,this.writeVarint(i),this.pos+=i},writeMessage:function(t,e,r){this.writeTag(t,Pbf.Bytes),this.writeRawMessage(e,r)},writePackedVarint:function(t,e){this.writeMessage(t,writePackedVarint,e)},writePackedSVarint:function(t,e){this.writeMessage(t,writePackedSVarint,e)},writePackedBoolean:function(t,e){this.writeMessage(t,writePackedBoolean,e)},writePackedFloat:function(t,e){this.writeMessage(t,writePackedFloat,e)},writePackedDouble:function(t,e){this.writeMessage(t,writePackedDouble,e)},writePackedFixed32:function(t,e){this.writeMessage(t,writePackedFixed32,e)},writePackedSFixed32:function(t,e){this.writeMessage(t,writePackedSFixed32,e)},writePackedFixed64:function(t,e){this.writeMessage(t,writePackedFixed64,e)},writePackedSFixed64:function(t,e){this.writeMessage(t,writePackedSFixed64,e)},writeBytesField:function(t,e){this.writeTag(t,Pbf.Bytes),this.writeBytes(e)},writeFixed32Field:function(t,e){this.writeTag(t,Pbf.Fixed32),this.writeFixed32(e)},writeSFixed32Field:function(t,e){this.writeTag(t,Pbf.Fixed32),this.writeSFixed32(e)},writeFixed64Field:function(t,e){this.writeTag(t,Pbf.Fixed64),this.writeFixed64(e)},writeSFixed64Field:function(t,e){this.writeTag(t,Pbf.Fixed64),this.writeSFixed64(e)},writeVarintField:function(t,e){this.writeTag(t,Pbf.Varint),this.writeVarint(e)},writeSVarintField:function(t,e){this.writeTag(t,Pbf.Varint),this.writeSVarint(e)},writeStringField:function(t,e){this.writeTag(t,Pbf.Bytes),this.writeString(e)},writeFloatField:function(t,e){this.writeTag(t,Pbf.Fixed32),this.writeFloat(e)},writeDoubleField:function(t,e){this.writeTag(t,Pbf.Fixed64),this.writeDouble(e)},writeBooleanField:function(t,e){this.writeVarintField(t,Boolean(e))}};var index$5=Point$1;Point$1.prototype={clone:function(){return new Point$1(this.x,this.y)},add:function(t){return this.clone()._add(t)},sub:function(t){return this.clone()._sub(t)},mult:function(t){return this.clone()._mult(t)},div:function(t){return this.clone()._div(t)},rotate:function(t){return this.clone()._rotate(t)},matMult:function(t){return this.clone()._matMult(t)},unit:function(){return this.clone()._unit()},perp:function(){return this.clone()._perp()},round:function(){return this.clone()._round()},mag:function(){return Math.sqrt(this.x*this.x+this.y*this.y)},equals:function(t){return this.x===t.x&&this.y===t.y},dist:function(t){return Math.sqrt(this.distSqr(t))},distSqr:function(t){var e=t.x-this.x,r=t.y-this.y;return e*e+r*r},angle:function(){return Math.atan2(this.y,this.x)},angleTo:function(t){return Math.atan2(this.y-t.y,this.x-t.x)},angleWith:function(t){return this.angleWithSep(t.x,t.y)},angleWithSep:function(t,e){return Math.atan2(this.x*e-this.y*t,this.x*t+this.y*e)},_matMult:function(t){var e=t[0]*this.x+t[1]*this.y,r=t[2]*this.x+t[3]*this.y;return this.x=e,this.y=r,this},_add:function(t){return this.x+=t.x,this.y+=t.y,this},_sub:function(t){return this.x-=t.x,this.y-=t.y,this},_mult:function(t){return this.x*=t,this.y*=t,this},_div:function(t){return this.x/=t,this.y/=t,this},_unit:function(){return this._div(this.mag()),this},_perp:function(){var t=this.y;return this.y=this.x,this.x=-t,this},_rotate:function(t){var e=Math.cos(t),r=Math.sin(t),i=e*this.x-r*this.y,n=r*this.x+e*this.y;return this.x=i,this.y=n,this},_round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this}},Point$1.convert=function(t){return t instanceof Point$1?t:Array.isArray(t)?new Point$1(t[0],t[1]):t};var Point=index$5,vectortilefeature=VectorTileFeature$2;VectorTileFeature$2.types=["Unknown","Point","LineString","Polygon"],VectorTileFeature$2.prototype.loadGeometry=function(){var t=this._pbf;t.pos=this._geometry;for(var e,r=t.readVarint()+t.pos,i=1,n=0,o=0,s=0,a=[];t.pos>3}if(n--,1===i||2===i)o+=t.readSVarint(),s+=t.readSVarint(),1===i&&(e&&a.push(e),e=[]),e.push(new Point(o,s));else{if(7!==i)throw new Error("unknown command "+i);e&&e.push(e[0].clone())}}return e&&a.push(e),a},VectorTileFeature$2.prototype.bbox=function(){var t=this._pbf;t.pos=this._geometry;for(var e=t.readVarint()+t.pos,r=1,i=0,n=0,o=0,s=1/0,a=-1/0,u=1/0,h=-1/0;t.pos>3}if(i--,1===r||2===r)n+=t.readSVarint(),o+=t.readSVarint(),na&&(a=n),oh&&(h=o);else if(7!==r)throw new Error("unknown command "+r)}return[s,u,a,h]},VectorTileFeature$2.prototype.toGeoJSON=function(t,e,r){function i(t){for(var e=0;e=this._features.length)throw new Error("feature index out of bounds");this._pbf.pos=this._features[t];var e=this._pbf.readVarint()+this._pbf.pos;return new VectorTileFeature$1(this._pbf,e,this.extent,this._keys,this._values)};var VectorTileLayer$1=vectortilelayer,vectortile=VectorTile$1,VectorTile=vectortile;L.SVG.Tile=L.SVG.extend({initialize:function(t,e,r){L.SVG.prototype.initialize.call(this,r),this._tileCoord=t,this._size=e,this._initContainer(),this._container.setAttribute("width",this._size.x),this._container.setAttribute("height",this._size.y),this._container.setAttribute("viewBox",[0,0,this._size.x,this._size.y].join(" ")),this._layers={}},getCoord:function(){return this._tileCoord},getContainer:function(){return this._container},onAdd:L.Util.falseFn,addTo:function(t){if(this._map=t,this.options.interactive)for(var e in this._layers){var r=this._layers[e];r._path.style.pointerEvents="auto",this._map._targets[L.stamp(r._path)]=r}},removeFrom:function(t){if(this.options.interactive)for(var e in this._layers){var r=this._layers[e];delete this._map._targets[L.stamp(r._path)]}delete this._map},_initContainer:function(){L.SVG.prototype._initContainer.call(this);L.SVG.create("rect")},_addPath:function(t){this._rootGroup.appendChild(t._path),this._layers[L.stamp(t)]=t},_updateIcon:function(t){var e=t._path=L.SVG.create("image"),r=t.options.icon,i=r.options,n=L.point(i.iconSize),o=i.iconAnchor||n&&n.divideBy(2,!0),s=t._point.subtract(o);e.setAttribute("x",s.x),e.setAttribute("y",s.y),e.setAttribute("width",n.x+"px"),e.setAttribute("height",n.y+"px"),e.setAttribute("href",i.iconUrl)}}),L.svg.tile=function(t,e,r){return new L.SVG.Tile(t,e,r)};var Symbolizer=L.Class.extend({render:function(t,e){this._renderer=t,this.options=e,t._initPath(this),t._updateStyle(this)},updateStyle:function(t,e){this.options=e,t._updateStyle(this)},_getPixelBounds:function(){for(var t=this._parts,e=L.bounds([]),r=0;rn&&(i=r,n=o);n>a?(e[i][2]=n,c.push(l),c.push(i),l=i):(u=c.pop(),l=c.pop())}}function getSqSegDist(e,t,r){var n=t[0],o=t[1],i=r[0],a=r[1],s=e[0],l=e[1],u=i-n,c=a-o;if(0!==u||0!==c){var f=((s-n)*u+(l-o)*c)/(u*u+c*c);f>1?(n=i,o=a):f>0&&(n+=u*f,o+=c*f)}return u=s-n,c=l-o,u*u+c*c}function convert$1(e,t){var r=[];if("FeatureCollection"===e.type)for(var n=0;n1?1:n,[r,n,0]}function calcSize(e){for(var t,r,n=0,o=0,i=0;i=r&&s<=n)return e;if(a>n||s=r&&f<=n)l.push(p);else if(!(c>n||f=t&&s<=r&&o.push(a)}return o}function clipGeometry(e,t,r,n,o,i){for(var a=[],s=0;sr?(x.push(o(l,h,t),o(l,h,r)),i||(x=newSlice(a,x,g,d,v))):p>=t&&x.push(o(l,h,t)):f>r?pr&&(x.push(o(l,h,r)),i||(x=newSlice(a,x,g,d,v))));l=m[y-1],f=l[n],f>=t&&f<=r&&x.push(l),c=x[x.length-1],i&&c&&(x[0][0]!==c[0]||x[0][1]!==c[1])&&x.push(x[0]),newSlice(a,x,g,d,v)}return a}function newSlice(e,t,r,n,o){return t.length&&(t.area=r,t.dist=n,void 0!==o&&(t.outer=o),e.push(t)),[]}function wrap$1(e,t,r){var n=e,o=clip$2(e,1,-1-t,t,0,r,-1,2),i=clip$2(e,1,1-t,2+t,0,r,-1,2);return(o||i)&&(n=clip$2(e,1,-t,1+t,0,r,-1,2),o&&(n=shiftFeatureCoords(o,1).concat(n)),i&&(n=n.concat(shiftFeatureCoords(i,-1)))),n}function shiftFeatureCoords(e,t){for(var r=[],n=0;na.max[0]&&(a.max[0]=u[0]),u[1]>a.max[1]&&(a.max[1]=u[1])}return a}function addFeature(e,t,r,n){var o,i,a,s,l=t.geometry,u=t.type,c=[],f=r*r;if(1===u)for(o=0;of)&&(p.push(s),e.numSimplified++),e.numPoints++;3===u&&rewind(p,a.outer),c.push(p)}else e.numPoints+=a.length;c.length&&e.features.push({geometry:c,type:u,tags:t.tags||null})}function rewind(e,t){signedArea(e)<0===t&&e.reverse()}function signedArea(e){for(var t,r,n=0,o=0,i=e.length,a=i-1;o1)return!1;var i=o.geometry[0].length;if(5!==i)return!1;for(var a=0;a1&&console.time("creation"),m=s.tiles[h]=createTile(e,p,r,n,g,t===u.maxZoom),s.tileCoords.push({z:t,x:r,y:n}),c)){c>1&&(console.log("tile z%d-%d-%d (features: %d, points: %d, simplified: %d)",t,r,n,m.numFeatures,m.numPoints,m.numSimplified),console.timeEnd("creation"));var d="z"+t;s.stats[d]=(s.stats[d]||0)+1,s.total++}if(m.source=e,o){if(t===u.maxZoom||t===o)continue;var v=1<1&&console.time("clipping");var y,x,b,M,P,S,w=.5*u.buffer/u.extent,$=.5-w,C=.5+w,F=1+w;y=x=b=M=null,P=clip(e,p,r-w,r+C,0,intersectX,m.min[0],m.max[0]),S=clip(e,p,r+$,r+F,0,intersectX,m.min[0],m.max[0]),P&&(y=clip(P,p,n-w,n+C,1,intersectY,m.min[1],m.max[1]),x=clip(P,p,n+$,n+F,1,intersectY,m.min[1],m.max[1])),S&&(b=clip(S,p,n-w,n+C,1,intersectY,m.min[1],m.max[1]),M=clip(S,p,n+$,n+F,1,intersectY,m.min[1],m.max[1])),c>1&&console.timeEnd("clipping"),y&&l.push(y,t+1,2*r,2*n),x&&l.push(x,t+1,2*r,2*n+1),b&&l.push(b,t+1,2*r+1,2*n),M&&l.push(M,t+1,2*r+1,2*n+1)}else o&&(f=t)}return f},GeoJSONVT.prototype.getTile=function(e,t,r){var n=this,o=this.options,i=o.extent,a=o.debug,s=1<1&&console.log("drilling down to z%d-%d-%d",e,t,r);for(var u,c=e,f=t,p=r;!u&&c>0;)c--,f=Math.floor(f/2),p=Math.floor(p/2),u=n.tiles[toID(c,f,p)];if(!u||!u.source)return null;if(a>1&&console.log("found parent tile z%d-%d-%d",c,f,p),isClippedSquare(u,i,o.buffer))return transform.tile(u,i);a>1&&console.time("drilling down");var h=this.splitTile(u.source,c,f,p,e,t,r);if(a>1&&console.timeEnd("drilling down"),null!==h){var m=1<c&&(c=s[0]),s[1]f&&(f=s[1])}function r(e){switch(e.type){case"GeometryCollection":e.geometries.forEach(r);break;case"Point":t(e.coordinates);break;case"MultiPoint":e.coordinates.forEach(t)}}var n=e.bbox;if(!n){var o,i,a=transform$3(e),s=new Array(2),l=1/0,u=l,c=-l,f=-l;e.arcs.forEach(function(e){for(var t=-1,r=e.length;++tc&&(c=s[0]),s[1]f&&(f=s[1])});for(i in e.objects)r(e.objects[i]);n=e.bbox=[l,u,c,f]}return n},reverse=function(e,t){for(var r,n=e.length,o=n-t;o<--n;)r=e[o],e[o++]=e[n],e[n]=r},feature=function(e,t){return"GeometryCollection"===t.type?{type:"FeatureCollection",features:t.geometries.map(function(t){return feature$1(e,t)})}:feature$1(e,t)},stitch=function(e,t){function r(t){var r,n=e.arcs[t<0?~t:t],o=n[0];return e.transform?(r=[0,0],n.forEach(function(e){r[0]+=e[0],r[1]+=e[1]})):r=n[n.length-1],t<0?[r,o]:[o,r]}function n(e,t){for(var r in e){var n=e[r];delete t[n.start],delete n.start,delete n.end,n.forEach(function(e){o[e<0?~e:e]=1}),s.push(n)}}var o={},i={},a={},s=[],l=-1;return t.forEach(function(r,n){var o,i=e.arcs[r<0?~r:r];i.length<3&&!i[1][0]&&!i[1][1]&&(o=t[++l],t[l]=r,t[n]=o)}),t.forEach(function(e){var t,n,o=r(e),s=o[0],l=o[1];if(t=a[s])if(delete a[t.end],t.push(e),t.end=l,n=i[l]){delete i[n.start];var u=n===t?t:t.concat(n);i[u.start=t.start]=a[u.end=n.end]=u}else i[t.start]=a[t.end]=t;else if(t=i[l])if(delete i[t.start],t.unshift(e),t.start=s,n=a[s]){delete a[n.end];var c=n===t?t:n.concat(t);i[c.start=n.start]=a[c.end=t.end]=c}else i[t.start]=a[t.end]=t;else t=[e],i[t.start=s]=a[t.end=l]=t}),n(a,i),n(i,a),t.forEach(function(e){o[e<0?~e:e]||s.push([e])}),s},bisect=function(e,t){for(var r=0,n=e.length;r>>1;e[o] rotate point -> add anchor) + */ + _updatePosition: function() { + if (!this._map) { return; } + divOverlayProto._updatePosition.apply(this, arguments); + if (this._map && this._map._rotate && this._zoomAnimated) { + var anchor = this._getAnchor(); + var pos = L.DomUtil.getPosition(this._container).subtract(anchor); + L.DomUtil.setPosition(this._container, this._map.rotatedPointToMapPanePoint(pos).add(anchor)); + } + + }, + + }); + + /** + * @external L.Popup + * + * @see https://github.com/Leaflet/Leaflet/tree/v1.9.3/src/layer/Popup.js + */ + + const popupProto = L.extend({}, L.Popup.prototype); + + L.Popup.include({ + + /** + * 0. update element anchor point (popupProto v1.9.3) + * 1. rotate around anchor point (subtract anchor -> rotate point -> add anchor) + */ + _animateZoom: function(e) { + popupProto._animateZoom.apply(this, arguments); + if (this._map && this._map._rotate) { + var anchor = this._getAnchor(); + var pos = L.DomUtil.getPosition(this._container).subtract(anchor); + L.DomUtil.setPosition(this._container, this._map.rotatedPointToMapPanePoint(pos).add(anchor)); + } + }, + + /** + * Fix for L.popup({ keepInView = true }) + * + * @see https://github.com/fnicollet/Leaflet/pull/21 + */ + _adjustPan: function() { + if (!this.options.autoPan || (this._map._panAnim && this._map._panAnim._inProgress)) { return; } + + // We can endlessly recurse if keepInView is set and the view resets. + // Let's guard against that by exiting early if we're responding to our own autopan. + if (this._autopanning) { + this._autopanning = false; + return; + } + + var map = this._map, + marginBottom = parseInt(L.DomUtil.getStyle(this._container, 'marginBottom'), 10) || 0, + containerHeight = this._container.offsetHeight + marginBottom, + containerWidth = this._containerWidth, + layerPos = new L.Point(this._containerLeft, -containerHeight - this._containerBottom); + + layerPos._add(L.DomUtil.getPosition(this._container)); + + /** @TODO use popupProto._adjustPan */ + // var containerPos = map.layerPointToContainerPoint(layerPos); + var containerPos = layerPos._add(this._map._getMapPanePos()), + padding = L.point(this.options.autoPanPadding), + paddingTL = L.point(this.options.autoPanPaddingTopLeft || padding), + paddingBR = L.point(this.options.autoPanPaddingBottomRight || padding), + size = map.getSize(), + dx = 0, + dy = 0; + + if (containerPos.x + containerWidth + paddingBR.x > size.x) { // right + dx = containerPos.x + containerWidth - size.x + paddingBR.x; + } + if (containerPos.x - dx - paddingTL.x < 0) { // left + dx = containerPos.x - paddingTL.x; + } + if (containerPos.y + containerHeight + paddingBR.y > size.y) { // bottom + dy = containerPos.y + containerHeight - size.y + paddingBR.y; + } + if (containerPos.y - dy - paddingTL.y < 0) { // top + dy = containerPos.y - paddingTL.y; + } + + // @namespace Map + // @section Popup events + // @event autopanstart: Event + // Fired when the map starts autopanning when opening a popup. + if (dx || dy) { + // Track that we're autopanning, as this function will be re-ran on moveend + if (this.options.keepInView) { + this._autopanning = true; + } + map + .fire('autopanstart') + .panBy([dx, dy]); + } + }, + + }); + + /** + * @external L.Tooltip + * + * @see https://github.com/Leaflet/Leaflet/tree/v1.9.3/src/layer/Tooltip.js + */ + + const tooltipProto = L.extend({}, L.Tooltip.prototype); + + L.Tooltip.include({ + + _animateZoom: function(e) { + if (!this._map._rotate) { + return tooltipProto._animateZoom.apply(this, arguments); + } + var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center); + + pos = this._map.rotatedPointToMapPanePoint(pos); + this._setPosition(pos); + }, + + _updatePosition: function() { + if (!this._map._rotate) { + return tooltipProto._updatePosition.apply(this, arguments); + } + var pos = this._map.latLngToLayerPoint(this._latlng); + + pos = this._map.rotatedPointToMapPanePoint(pos); + this._setPosition(pos); + }, + + }); + + /** + * @external L.Icon + * + * @see https://github.com/Leaflet/Leaflet/tree/v1.9.3/src/layer/marker/Icon.js + */ + + L.extend({}, L.Icon.prototype); + + L.Icon.include({ + + _setIconStyles: function(img, name) { + var options = this.options; + var sizeOption = options[name + 'Size']; + + if (typeof sizeOption === 'number') { + sizeOption = [sizeOption, sizeOption]; + } + + var size = L.point(sizeOption), + anchor = L.point(name === 'shadow' && options.shadowAnchor || options.iconAnchor || + size && size.divideBy(2, true)); + + img.className = 'leaflet-marker-' + name + ' ' + (options.className || ''); + + if (anchor) { + img.style.marginLeft = (-anchor.x) + 'px'; + img.style.marginTop = (-anchor.y) + 'px'; + /** @TODO use iconProto._setIconStyles */ + img.style[L.DomUtil.TRANSFORM + "Origin"] = anchor.x + "px " + anchor.y + "px 0px"; + } + + if (size) { + img.style.width = size.x + 'px'; + img.style.height = size.y + 'px'; + } + }, + + }); + + /** + * @external L.Marker + * @external L.Handler.MarkerDrag + * + * @see https://github.com/Leaflet/Leaflet/tree/v1.9.3/src/layer/marker/Marker.js + * @see https://github.com/Leaflet/Leaflet/tree/v1.9.3/src/layer/marker/Marker.Drag.js + * @see https://github.com/Leaflet/Leaflet/tree/v1.9.3/src/dom/Draggable.js + */ + + const markerProto = L.extend({}, L.Marker.prototype); + + L.Marker.mergeOptions({ + + /** + * Rotation of this marker in rad + * + * @type {Number} + */ + rotation: 0, + + /** + * Rotate this marker when map rotates + * + * @type {Boolean} + */ + rotateWithView: false, + + /** + * Scale of the marker icon + * + * @type {Number} + */ + scale: undefined, + + }); + + var markerDragProto; // retrived at runtime (see below: L.Marker::_initInteraction()) + + var MarkerDrag = { + + // _onDragStart: function() { + // if (!this._marker._map._rotate) { + // return markerDragProto._onDragStart.apply(this, arguments); + // } + // this._draggable.updateMapBearing(this._marker._map._bearing); + // }, + + _onDrag: function(e) { + var marker = this._marker, + /** @TODO use markerDragProto._onDrag */ + rotated_marker = marker.options.rotation || marker.options.rotateWithView, + shadow = marker._shadow, + iconPos = L.DomUtil.getPosition(marker._icon); + + /** @TODO use markerDragProto._onDrag */ + // update shadow position + if (!rotated_marker && shadow) { + L.DomUtil.setPosition(shadow, iconPos); + } + + /** @TODO use markerDragProto._onDrag */ + if (marker._map._rotate) { + // Reverse calculation from mapPane coordinates to rotatePane coordinates + iconPos = marker._map.mapPanePointToRotatedPoint(iconPos); + } + var latlng = marker._map.layerPointToLatLng(iconPos); + + marker._latlng = latlng; + e.latlng = latlng; + e.oldLatLng = this._oldLatLng; + + /** @TODO use markerDragProto._onDrag */ + if (rotated_marker) marker.setLatLng(latlng); // use `setLatLng` to presisit rotation. low efficiency + else marker.fire('move', e); // `setLatLng` will trig 'move' event. we imitate here. + + // @event drag: Event + // Fired repeatedly while the user drags the marker. + marker + .fire('drag', e); + }, + + _onDragEnd: function(e) { + if (this._marker._map._rotate) { + this._marker.update(); + } + markerDragProto._onDragEnd.apply(this, arguments); + }, + + }; + + L.Marker.include({ + + /** + * Update L.Marker anchor position after the map + * is moved by calling `map.setBearing(theta)` + * + * @listens L.Map~rotate + */ + getEvents: function() { + return L.extend(markerProto.getEvents.apply(this, arguments), { rotate: this.update }); + }, + + _initInteraction: function() { + var ret = markerProto._initInteraction.apply(this, arguments); + if (this.dragging && this.dragging.enabled() && this._map && this._map._rotate) { + // L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable + markerDragProto = markerDragProto || Object.getPrototypeOf(this.dragging); + this.dragging.disable(); + Object.assign(this.dragging, { + // _onDragStart: MarkerDrag._onDragStart.bind(this.dragging), + _onDrag: MarkerDrag._onDrag.bind(this.dragging), + _onDragEnd: MarkerDrag._onDragEnd.bind(this.dragging), + }); + this.dragging.enable(); + } + return ret; + }, + + _setPos: function(pos) { + + /** @TODO use markerProto._setPos */ + if (this._map._rotate) { + pos = this._map.rotatedPointToMapPanePoint(pos); + } + + /** @TODO use markerProto._setPos */ + var bearing = this.options.rotation || 0; + if (this.options.rotateWithView) { + bearing += this._map._bearing; + } + + /** @TODO use markerProto._setPos */ + if (this._icon) { + L.DomUtil.setPosition(this._icon, pos, bearing, pos, this.options.scale); + } + + /** @TODO use markerProto._setPos */ + if (this._shadow) { + L.DomUtil.setPosition(this._shadow, pos, bearing, pos, this.options.scale); + } + + this._zIndex = pos.y + this.options.zIndexOffset; + + this._resetZIndex(); + }, + + // _updateZIndex: function(offset) { + // if (!this._map._rotate) { + // return markerProto._updateZIndex.apply(this, arguments); + // } + // this._icon.style.zIndex = Math.round(this._zIndex + offset); + // }, + + setRotation: function(rotation) { + this.options.rotation = rotation; + this.update(); + }, + + }); + + /** + * @external L.GridLayer + * + * @see https://github.com/Leaflet/Leaflet/tree/v1.9.3/src/layer/tile/GridLayer.js + */ + + const gridLayerProto = L.extend({}, L.GridLayer.prototype); + + L.GridLayer.include({ + + /** + * Redraw L.TileLayer bounds after the map is + * moved by just calling `map.setBearing(theta)` + * + * @listens L.Map~rotate + */ + getEvents: function() { + var events = gridLayerProto.getEvents.apply(this, arguments); + if (this._map._rotate && !this.options.updateWhenIdle) { + if (!this._onRotate) { + this._onRotate = L.Util.throttle(this._onMoveEnd, this.options.updateInterval, this); + } + events.rotate = this._onRotate; + } + return events; + }, + + _getTiledPixelBounds: function(center) { + if (!this._map._rotate) { + return gridLayerProto._getTiledPixelBounds.apply(this, arguments); + } + + return this._map._getNewPixelBounds(center, this._tileZoom); + }, + + }); + + /** + * @external L.Renderer + * + * @see https://github.com/Leaflet/Leaflet/tree/v1.9.3/src/layer/vector/Renderer.js + */ + + const rendererProto = L.extend({}, L.Renderer.prototype); + + L.Renderer.include({ + + /** + * Redraw L.Canvas and L.SVG renderer bounds after the + * map is moved by just calling `map.setBearing(theta)` + * + * @listens L.Map~rotate + */ + getEvents: function() { + return L.extend(rendererProto.getEvents.apply(this, arguments), { rotate: this._update }); + }, + + /** + * Fix for `map.flyTo()` when `false === map.options.zoomAnimation` + * + * @see https://github.com/Leaflet/Leaflet/pull/8794 + */ + onAdd: function() { + rendererProto.onAdd.apply(this, arguments); + if (L.version <= "1.9.3") { + // always keep transform-origin as 0 0 + this._container.classList.add('leaflet-zoom-animated'); + } + }, + + /** + * @FIXME layer drifts on `map.setZoom()` (eg. zoom during animation) + * + * the main cause seems to be related to `this._updateTransform(path._center, path._zoom))` + * and `this._topLeft = this._map.layerPointToLatLng(this._bounds.min);` + * + * @example + * map.setZoom(2); + * path._renderer._update(); + * path._renderer._updateTransform(path._renderer._center, path._renderer._zoom); + * + * @see https://github.com/Leaflet/Leaflet/pull/8794 + * @see https://github.com/Leaflet/Leaflet/pull/8103 + * @see https://github.com/Leaflet/Leaflet/issues/7466 + * + * @TODO rechek this changes from leaflet@v1.9.3 + * + * @see https://github.com/Leaflet/Leaflet/compare/v1.7.0...v1.9.3 + */ + _updateTransform: function(center, zoom) { + if (!this._map._rotate) { + return rendererProto._updateTransform.apply(this, arguments); + } + /** + * @FIXME see path._renderer._reset(); + */ + var scale = this._map.getZoomScale(zoom, this._zoom), + offset = this._map._latLngToNewLayerPoint(this._topLeft, zoom, center); + + L.DomUtil.setTransform(this._container, offset, scale); + + }, + + // getEvents() { + // const events = { + // viewreset: this._reset, + // zoom: this._onZoom, + // moveend: this._update, + // zoomend: this._onZoomEnd + // }; + // if (this._zoomAnimated) { + // events.zoomanim = this._onAnimZoom; + // } + // return events; + // }, + + // _onAnimZoom(ev) { + // this._updateTransform(ev.center, ev.zoom); + // }, + + // _onZoom() { + // this._updateTransform(this._map.getCenter(), this._map.getZoom()); + // }, + + // _onZoomEnd() { + // for (const id in this._layers) { + // this._layers[id]._project(); + // } + // }, + + // _reset() { + // this._update(); + // this._updateTransform(this._center, this._zoom); + + // for (const id in this._layers) { + // this._layers[id]._reset(); + // } + // }, + + // _updatePaths() { + // for (const id in this._layers) { + // this._layers[id]._update(); + // } + // }, + + _update: function() { + if (!this._map._rotate) { + return rendererProto._update.apply(this, arguments); + } + // Update pixel bounds of renderer container (for positioning/sizing/clipping later) + // Subclasses are responsible of firing the 'update' event. + this._bounds = this._map._getPaddedPixelBounds(this.options.padding); + this._topLeft = this._map.layerPointToLatLng(this._bounds.min); + this._center = this._map.getCenter(); + this._zoom = this._map.getZoom(); + }, + + }); + + /** + * @external L.Map + * + * @see https://github.com/Leaflet/Leaflet/blob/v1.9.3/src/map/Map.js + */ + + const mapProto = L.extend({}, L.Map.prototype); + + L.Map.mergeOptions({ rotate: false, bearing: 0, }); + + L.Map.include({ + + /** + * @param {(HTMLElement|String)} id html selector + * @param {Object} [options={}] leaflet map options + */ + initialize: function(id, options) { + if (options.rotate) { + this._rotate = true; + this._bearing = 0; + } + mapProto.initialize.apply(this, arguments); + if(this.options.rotate){ + this.setBearing(this.options.bearing); + } + }, + + /** + * Given a pixel coordinate relative to the map container, + * returns the corresponding pixel coordinate relative to + * the [origin pixel](#map-getpixelorigin). + * + * @param {L.Point} point pixel screen coordinates + * @returns {L.Point} transformed pixel point + */ + containerPointToLayerPoint: function(point) { + if (!this._rotate) { + return mapProto.containerPointToLayerPoint.apply(this, arguments); + } + return L.point(point) + .subtract(this._getMapPanePos()) + .rotateFrom(-this._bearing, this._getRotatePanePos()) + .subtract(this._getRotatePanePos()); + }, + + /** + * Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin), + * returns the corresponding pixel coordinate relative to the map container. + * + * @param {L.Point} point pixel screen coordinates + * @returns {L.Point} transformed pixel point + */ + layerPointToContainerPoint: function(point) { + if (!this._rotate) { + return mapProto.layerPointToContainerPoint.apply(this, arguments); + } + return L.point(point) + .add(this._getRotatePanePos()) + .rotateFrom(this._bearing, this._getRotatePanePos()) + .add(this._getMapPanePos()); + }, + + /** + * Converts a coordinate from the rotated pane reference system + * to the reference system of the not rotated map pane. + * + * (rotatePane) --> (mapPane) + * (rotatePane) --> (norotatePane) + * + * @param {L.Point} point pixel screen coordinates + * @returns {L.Point} + * + * @since leaflet-rotate (v0.1) + */ + rotatedPointToMapPanePoint: function(point) { + return L.point(point) + .rotate(this._bearing) + ._add(this._getRotatePanePos()); + }, + + /** + * Converts a coordinate from the not rotated map pane reference system + * to the reference system of the rotated pane. + * + * (mapPane) --> (rotatePane) + * (norotatePane) --> (rotatePane) + * + * @param {L.Point} point pixel screen coordinates + * + * @since leaflet-rotate (v0.1) + */ + mapPanePointToRotatedPoint: function(point) { + return L.point(point) + ._subtract(this._getRotatePanePos()) + .rotate(-this._bearing); + }, + + // latLngToLayerPoint: function (latlng) { + // var projectedPoint = this.project(L.latLng(latlng))._round(); + // return projectedPoint._subtract(this.getPixelOrigin()); + // }, + + // latLngToContainerPoint: function (latlng) { + // return this.layerPointToContainerPoint(this.latLngToLayerPoint(toLatLng(latlng))); + // }, + + /** + * Given latlng bounds, returns the bounds in projected pixel + * relative to the map container. + * + * @see https://github.com/ronikar/Leaflet/blob/5c480ef959b947c3beed7065425a5a36c486262b/src/map/Map.js#L1114-L1135 + * + * @param {L.LatLngBounds} bounds + * @returns {L.Bounds} + * + * @since leaflet-rotate (v0.2) + */ + mapBoundsToContainerBounds: function (bounds) { + if (!this._rotate && mapProto.mapBoundsToContainerBounds) { + return mapProto.mapBoundsToContainerBounds.apply(this, arguments); + } + + // const nw = this.latLngToContainerPoint(bounds.getNorthWest()), + // ne = this.latLngToContainerPoint(bounds.getNorthEast()), + // sw = this.latLngToContainerPoint(bounds.getSouthWest()), + // se = this.latLngToContainerPoint(bounds.getSouthEast()); + + // same as `this.latLngToContainerPoint(latlng)` but with floating point precision + const origin = this.getPixelOrigin(); + const nw = this.layerPointToContainerPoint(this.project(bounds.getNorthWest())._subtract(origin)), + ne = this.layerPointToContainerPoint(this.project(bounds.getNorthEast())._subtract(origin)), + sw = this.layerPointToContainerPoint(this.project(bounds.getSouthWest())._subtract(origin)), + se = this.layerPointToContainerPoint(this.project(bounds.getSouthEast())._subtract(origin)); + + return L.bounds([ + L.point(Math.min(nw.x, ne.x, se.x, sw.x), Math.min(nw.y, ne.y, se.y, sw.y)), // [ minX, minY ] + L.point(Math.max(nw.x, ne.x, se.x, sw.x), Math.max(nw.y, ne.y, se.y, sw.y)) // [ maxX, maxY ] + ]); + }, + + /** + * Returns geographical bounds visible in the current map view + * + * @TODO find out if map bounds calculated by `L.Map::getBounds()` + * function should match the `rotatePane` or `norotatePane` bounds + * + * @see https://github.com/fnicollet/Leaflet/issues/7 + * + * @returns {L.LatLngBounds} + */ + getBounds: function() { + if (!this._rotate) { + return mapProto.getBounds.apply(this, arguments); + } + + // SEE: https://github.com/fnicollet/Leaflet/pull/22 + // + // var bounds = this.getPixelBounds(), + // sw = this.unproject(bounds.getBottomLeft()), + // ne = this.unproject(bounds.getTopRight()); + // return new LatLngBounds(sw, ne); + // + + // LatLngBounds' constructor automatically + // extends the bounds to fit the passed points + var size = this.getSize(); + return new L.LatLngBounds([ + this.containerPointToLatLng([0, 0]), // topleft + this.containerPointToLatLng([size.x, 0]), // topright + this.containerPointToLatLng([size.x, size.y]), // bottomright + this.containerPointToLatLng([0, size.y]), // bottomleft + ]); + }, + + /** + * Returns the bounds of the current map view in projected pixel + * coordinates (sometimes useful in layer and overlay implementations). + * + * @TODO find out if map bounds calculated by `L.Map::getPixelBounds()` + * function should match the `rotatePane` or `norotatePane` bounds + * + * @see https://github.com/fnicollet/Leaflet/issues/7 + * + * @returns {L.Bounds} + */ + // getPixelBounds(center, zoom) { + // // const topLeftPoint = map.containerPointToLayerPoint(this._getTopLeftPoint()); + // const topLeftPoint = this._getTopLeftPoint(center, zoom); + // return new L.Bounds(topLeftPoint, topLeftPoint.add(this.getSize())); + // }, + + /** + * Change map rotation + * + * @param {number} theta map degrees + * + * @since leaflet-rotate (v0.1) + */ + setBearing: function(theta) { + if (!L.Browser.any3d || !this._rotate) { return; } + + var bearing = L.Util.wrapNum(theta, [0, 360]) * L.DomUtil.DEG_TO_RAD, + center = this._getPixelCenter(), + oldPos = this._getRotatePanePos().rotateFrom(-this._bearing, center), + newPos = oldPos.rotateFrom(bearing, center); + + // CSS transform + L.DomUtil.setPosition(this._rotatePane, oldPos, bearing, center); + + this._pivot = center; + this._bearing = bearing; + this._rotatePanePos = newPos; + + this.fire('rotate'); + }, + + /** + * Get current map rotation + * + * @returns {number} theta map degrees + * + * @since leaflet-rotate (v0.1) + */ + getBearing: function() { + return this._bearing * L.DomUtil.RAD_TO_DEG; + }, + + /** + * Creates a new [map pane](#map-pane) with the given name if it doesn't + * exist already, then returns it. The pane is created as a child of + * `container`, or as a child of the main map pane if not set. + * + * @param {String} name leaflet pane + * @param {HTMLElement} [container] parent element + * @returns {HTMLElement} pane container + */ + // createPane: function(name, container) { + // if (!this._rotate || name == 'mapPane') { + // return mapProto.createPane.apply(this, arguments); + // } + // // init "rotatePane" + // if (!this._rotatePane) { + // // this._pivot = this.getSize().divideBy(2); + // this._rotatePane = mapProto.createPane.call(this, 'rotatePane', this._mapPane); + // L.DomUtil.setPosition(this._rotatePane, new L.Point(0, 0), this._bearing, this._pivot); + // } + // return mapProto.createPane.call(this, name, container || this._rotatePane); + // }, + + /** + * Panes are DOM elements used to control the ordering of layers on + * the map. You can access panes with [`map.getPane`](#map-getpane) + * or [`map.getPanes`](#map-getpanes) methods. New panes can be created + * with the [`map.createPane`](#map-createpane) method. + * + * Every map has the following default panes that differ only in zIndex: + * + * - mapPane [HTMLElement = 'auto'] - Pane that contains all other map panes + * - tilePane [HTMLElement = 2] - Pane for tile layers + * - overlayPane [HTMLElement = 4] - Pane for overlays like polylines and polygons + * - shadowPane [HTMLElement = 5] - Pane for overlay shadows (e.g. marker shadows) + * - markerPane [HTMLElement = 6] - Pane for marker icons + * - tooltipPane [HTMLElement = 650] - Pane for tooltips. + * - popupPane [HTMLElement = 700] - Pane for popups. + */ + _initPanes: function() { + var panes = this._panes = {}; + this._paneRenderers = {}; + + this._mapPane = this.createPane('mapPane', this._container); + L.DomUtil.setPosition(this._mapPane, new L.Point(0, 0)); + + if (this._rotate) { + this._rotatePane = this.createPane('rotatePane', this._mapPane); + this._norotatePane = this.createPane('norotatePane', this._mapPane); + // rotatePane + this.createPane('tilePane', this._rotatePane); + this.createPane('overlayPane', this._rotatePane); + // norotatePane + this.createPane('shadowPane', this._norotatePane); + this.createPane('markerPane', this._norotatePane); + this.createPane('tooltipPane', this._norotatePane); + this.createPane('popupPane', this._norotatePane); + } else { + this.createPane('tilePane'); + this.createPane('overlayPane'); + this.createPane('shadowPane'); + this.createPane('markerPane'); + this.createPane('tooltipPane'); + this.createPane('popupPane'); + } + + if (!this.options.markerZoomAnimation) { + L.DomUtil.addClass(panes.markerPane, 'leaflet-zoom-hide'); + L.DomUtil.addClass(panes.shadowPane, 'leaflet-zoom-hide'); + } + }, + + /** + * Pans the map the minimum amount to make the `latlng` visible. Use + * padding options to fit the display to more restricted bounds. + * If `latlng` is already within the (optionally padded) display bounds, + * the map will not be panned. + * + * @see https://github.com/Raruto/leaflet-rotate/issues/18 + * + * @param {L.LatLng} latlng coordinates + * @param {Object} [options={}] padding options + * + * @returns {L.Map} current map instance + */ + panInside(latlng, options) { + if (!this._rotate || Math.abs(this._bearing).toFixed(1) < 0.1) { + return mapProto.panInside.apply(this, arguments); + } + + options = options || {}; + + const paddingTL = L.point(options.paddingTopLeft || options.padding || [0, 0]), + paddingBR = L.point(options.paddingBottomRight || options.padding || [0, 0]), + /** @TODO use mapProto.panInside */ + // pixelPoint = this.project(latlng), + // pixelBounds = this.getPixelBounds(), + // pixelCenter = this.project(this.getCenter()), + rect = this._container.getBoundingClientRect(), + pixelPoint = this.latLngToContainerPoint(latlng), + pixelBounds = L.bounds([ L.point(rect), L.point(rect).add(this.getSize()) ]), + pixelCenter = pixelBounds.getCenter(), + // + paddedBounds = L.bounds([pixelBounds.min.add(paddingTL), pixelBounds.max.subtract(paddingBR)]), + paddedSize = paddedBounds.getSize(); + + if (!paddedBounds.contains(pixelPoint)) { + this._enforcingBounds = true; + const centerOffset = pixelPoint.subtract(paddedBounds.getCenter()); + const offset = paddedBounds.extend(pixelPoint).getSize().subtract(paddedSize); + pixelCenter.x += centerOffset.x < 0 ? -offset.x : offset.x; + pixelCenter.y += centerOffset.y < 0 ? -offset.y : offset.y; + /** @TODO use mapProto.panInside */ + // this.panTo(this.unproject(pixelCenter), options); + this.panTo(this.containerPointToLatLng(pixelCenter), options); + // + this._enforcingBounds = false; + } + return this; + }, + + /** + * Pans the map to the closest view that would lie inside the given bounds + * (if it's not already), controlling the animation using the options specific, + * if any. + * + * @TODO check if map bounds calculated by `L.Map::panInsideBounds()` + * function should match the `rotatePane` or `norotatePane` bounds + * + * @see https://github.com/fnicollet/Leaflet/issues/7 + * + * @param {L.LatLngBounds} bounds coordinates + * @param {Object} [options] pan options + * @returns {L.Map} current map instance + */ + // panInsideBounds: function (bounds, options) { + // this._enforcingBounds = true; + // var center = this.getCenter(), + // newCenter = this._limitCenter(center, this._zoom, L.latLngBounds(bounds)); + // + // if (!center.equals(newCenter)) { + // this.panTo(newCenter, options); + // } + // + // this._enforcingBounds = false; + // return this; + // }, + + // adjust center for view to get inside bounds + // _limitCenter(center, zoom, bounds) { + // + // if (!bounds) { return center; } + // + // const centerPoint = this.project(center, zoom), + // viewHalf = this.getSize().divideBy(2), + // viewBounds = new Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)), + // offset = this._getBoundsOffset(viewBounds, bounds, zoom); + // + // // If offset is less than a pixel, ignore. + // // This prevents unstable projections from getting into + // // an infinite loop of tiny offsets. + // if (Math.abs(offset.x) <= 1 && Math.abs(offset.y) <= 1) { + // return center; + // } + // + // return this.unproject(centerPoint.add(offset), zoom); + // }, + + // @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this + // Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto), + // but takes a bounds parameter like [`fitBounds`](#map-fitbounds). + // flyToBounds(bounds, options) { + // const target = this._getBoundsCenterZoom(bounds, options); + // return this.flyTo(target.center, target.zoom, options); + // }, + + // _getBoundsCenterZoom(bounds, options) { + // + // options = options || {}; + // bounds = bounds.getBounds ? bounds.getBounds() : toLatLngBounds(bounds); + // + // const paddingTL = L.point(options.paddingTopLeft || options.padding || [0, 0]), + // paddingBR = L.point(options.paddingBottomRight || options.padding || [0, 0]); + // + // let zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR)); + // + // zoom = (typeof options.maxZoom === 'number') ? Math.min(options.maxZoom, zoom) : zoom; + // + // if (zoom === Infinity) { + // return { center: bounds.getCenter(), zoom }; + // } + // + // return { center, zoom }; + // + // }, + + /** + * Returns the maximum zoom level on which the given bounds fit to the map + * view in its entirety. If `inside` (optional) is set to `true`, the method + * instead returns the minimum zoom level on which the map view fits into + * the given bounds in its entirety. + * + * @param {L.LatLngBounds} bounds + * @param {Boolean} [inside=false] + * @param {L.Point} [padding=[0,0]] + * + * @returns {Number} zoom level + */ + getBoundsZoom(bounds, inside, padding) { + if (!this._rotate || Math.abs(this._bearing).toFixed(1) < 0.1) { + return mapProto.getBoundsZoom.apply(this, arguments); + } + + bounds = L.latLngBounds(bounds); + padding = L.point(padding || [0, 0]); + + let zoom = this.getZoom() || 0; + const min = this.getMinZoom(), + max = this.getMaxZoom(), + /** @TODO use mapProto.getBoundsZoom */ + // nw = bounds.getNorthWest(), + // se = bounds.getSouthEast(), + // size = this.getSize().subtract(padding), + // boundsSize = L.bounds(this.project(se, zoom), this.project(nw, zoom)).getSize(), + size = this.getSize().subtract(padding), + boundsSize = this.mapBoundsToContainerBounds(bounds).getSize(), + snap = this.options.zoomSnap, + scalex = size.x / boundsSize.x, + scaley = size.y / boundsSize.y, + scale = inside ? Math.max(scalex, scaley) : Math.min(scalex, scaley); + + zoom = this.getScaleZoom(scale, zoom); + + if (snap) { + zoom = Math.round(zoom / (snap / 100)) * (snap / 100); // don't jump if within 1% of a snap level + zoom = inside ? Math.ceil(zoom / snap) * snap : Math.floor(zoom / snap) * snap; + } + + return Math.max(min, Math.min(max, zoom)); + }, + + /** + * Layer point of the current center + * + * @returns {L.Point} layer center + */ + // _getCenterLayerPoint: function () { + // return this.containerPointToLayerPoint(this.getSize()._divideBy(2)); + // }, + + /** + * Offset of the specified place to the current center in pixels + * + * @param {L.LatLng} latlng map coordinates + */ + _getCenterOffset: function(latlng) { + var centerOffset = mapProto._getCenterOffset.apply(this, arguments); + if (this._rotate) { + centerOffset = centerOffset.rotate(this._bearing); + } + return centerOffset; + }, + + /** + * @since leaflet-rotate (v0.1) + */ + _getRotatePanePos: function() { + return this._rotatePanePos || new L.Point(0, 0); + // return L.DomUtil.getPosition(this._rotatePane) || new L.Point(0, 0); + }, + + // _latLngToNewLayerPoint(latlng, zoom, center) { + // const topLeft = this._getNewPixelOrigin(center, zoom); + // return this.project(latlng, zoom)._subtract(topLeft); + //}, + + _getNewPixelOrigin: function(center, zoom) { + if (!this._rotate) { + return mapProto._getNewPixelOrigin.apply(this, arguments); + } + var viewHalf = this.getSize()._divideBy(2); + return this.project(center, zoom) + .rotate(this._bearing) + ._subtract(viewHalf) + ._add(this._getMapPanePos()) + ._add(this._getRotatePanePos()) + .rotate(-this._bearing) + ._round(); + }, + + /** + * @since leaflet-rotate (v0.2) + * + * @see src\layer\tile\GridLayer::_getTiledPixelBounds() + */ + _getNewPixelBounds: function(center, zoom) { + center = center || this.getCenter(); + zoom = zoom || this.getZoom(); + if (!this._rotate && mapProto._getNewPixelBounds) { + return mapProto._getNewPixelBounds.apply(this, arguments); + } + var mapZoom = this._animatingZoom ? Math.max(this._animateToZoom, this.getZoom()) : this.getZoom(), + scale = this.getZoomScale(mapZoom, zoom), + pixelCenter = this.project(center, zoom).floor(), + size = this.getSize(), + halfSize = new L.Bounds([ + this.containerPointToLayerPoint([0, 0]).floor(), + this.containerPointToLayerPoint([size.x, 0]).floor(), + this.containerPointToLayerPoint([0, size.y]).floor(), + this.containerPointToLayerPoint([size.x, size.y]).floor() + ]).getSize().divideBy(scale * 2); + + return new L.Bounds(pixelCenter.subtract(halfSize), pixelCenter.add(halfSize)); + }, + + /** + * @since leaflet-rotate (v0.2) + * + * @return {L.Point} map pivot point (center) + */ + _getPixelCenter: function() { + if (!this._rotate && mapProto._getPixelCenter) { + return mapProto._getPixelCenter.apply(this, arguments); + } + return this.getSize()._divideBy(2)._subtract(this._getMapPanePos()); + }, + + /** + * @since leaflet-rotate (v0.2) + * + * @see src\layer\vector\Renderer::_update() + */ + _getPaddedPixelBounds: function(padding) { + if (!this._rotate && mapProto._getPaddedPixelBounds) { + return mapProto._getPaddedPixelBounds.apply(this, arguments); + } + var p = padding, + size = this.getSize(), + padMin = size.multiplyBy(-p), + padMax = size.multiplyBy(1 + p); + //min = this.containerPointToLayerPoint(size.multiplyBy(-p)).round(); + + return new L.Bounds([ + this.containerPointToLayerPoint([padMin.x, padMin.y]).floor(), + this.containerPointToLayerPoint([padMin.x, padMax.y]).floor(), + this.containerPointToLayerPoint([padMax.x, padMin.y]).floor(), + this.containerPointToLayerPoint([padMax.x, padMax.y]).floor() + ]); + }, + + _handleGeolocationResponse: function(pos) { + if (!this._container._leaflet_id) { return; } + + var lat = pos.coords.latitude, + lng = pos.coords.longitude, + /** @TODO use mapProto._handleGeolocationResponse */ + hdg = pos.coords.heading, + latlng = new L.LatLng(lat, lng), + bounds = latlng.toBounds(pos.coords.accuracy), + options = this._locateOptions; + + if (options.setView) { + var zoom = this.getBoundsZoom(bounds); + this.setView(latlng, options.maxZoom ? Math.min(zoom, options.maxZoom) : zoom); + } + + var data = { + latlng: latlng, + bounds: bounds, + timestamp: pos.timestamp, + /** @TODO use mapProto._handleGeolocationResponse */ + heading: hdg + }; + + for (var i in pos.coords) { + if (typeof pos.coords[i] === 'number') { + data[i] = pos.coords[i]; + } + } + + // @event locationfound: LocationEvent + // Fired when geolocation (using the [`locate`](#map-locate) method) + // went successfully. + this.fire('locationfound', data); + }, + + /** + * @see https://github.com/ronikar/Leaflet/blob/5c480ef959b947c3beed7065425a5a36c486262b/src/geo/LatLngBounds.js#L253-L264 + * + * @param {L.Bounds} points + * @returns {L.Bounds} + */ + // toCircumscribedBounds(points) { + // var minX = points.reduce(function (pv, v) { return Math.min(pv, v.x); }, points[0].x), + // maxX = points.reduce(function (pv, v) { return Math.max(pv, v.x); }, points[0].x), + // minY = points.reduce(function (pv, v) { return Math.min(pv, v.y); }, points[0].y), + // maxY = points.reduce(function (pv, v) { return Math.max(pv, v.y); }, points[0].y); + // + // return L.bounds(L.point(minX, minY), L.point(maxX, maxY)); + // }, + + }); + + /** + * Rotates the map according to a smartphone's compass. + * + * @typedef L.Map.CompassBearing + */ + + L.Map.CompassBearing = L.Handler.extend({ + + initialize: function(map) { + this._map = map; + /** @see https://caniuse.com/?search=DeviceOrientation */ + if ('ondeviceorientationabsolute' in window) { + this.__deviceOrientationEvent = 'deviceorientationabsolute'; + } else if('ondeviceorientation' in window) { + this.__deviceOrientationEvent = 'deviceorientation'; + } + this._throttled = L.Util.throttle(this._onDeviceOrientation, 100, this); + }, + + addHooks: function() { + if (this._map._rotate && this.__deviceOrientationEvent) { + L.DomEvent.on(window, this.__deviceOrientationEvent, this._throttled, this); + } else { + // L.Map.CompassBearing handler will be automatically + // disabled if device orientation is not supported. + this.disable(); + } + }, + + removeHooks: function() { + if (this._map._rotate && this.__deviceOrientationEvent) { + L.DomEvent.off(window, this.__deviceOrientationEvent, this._throttled, this); + } + }, + + /** + * `DeviceOrientationEvent.absolute` - Indicates whether the device is providing absolute + * orientation values (relatives to Magnetic North) or + * using some arbitrary frame determined by the device. + * + * `DeviceOrientationEvent.alpha` - Returns the rotation of the device around the Z axis; + * that is, the number of degrees by which the device is + * being twisted around the center of the screen. + * + * `window.orientation` - Returns the screen orientation in degrees (in 90-degree increments) + * of the viewport relative to the device's natural orientation. + * Its only possible values are -90, 0, 90, and 180. Positive + * values are counterclockwise; negative values are clockwise. + * + * @see https://developer.mozilla.org/en-US/docs/Web/API/DeviceOrientationEvent/absolute + * @see https://developer.mozilla.org/en-US/docs/Web/API/DeviceOrientationEvent/alpha + * @see https://developer.mozilla.org/en-US/docs/Web/API/Window/orientation + */ + _onDeviceOrientation: function(e) { + var angle = e.webkitCompassHeading || e.alpha; + var deviceOrientation = 0; + + // Safari iOS + if (!e.absolute && e.webkitCompassHeading) { + angle = 360 - angle; + } + + // Older browsers + if (!e.absolute && 'undefined' !== typeof window.orientation) { + deviceOrientation = window.orientation; + } + + this._map.setBearing(angle - deviceOrientation); + }, + + }); + + /** + * Add Compass bearing handler to L.Map (disabled unless `window.DeviceOrientationEvent` is set). + * + * @property {L.Map.CompassBearing} compassBearing + */ + L.Map.addInitHook('addHandler', 'compassBearing', L.Map.CompassBearing); + + /** + * Triggers `invalidateResize` when the map's DOM container mutates. + * + * @typedef L.Map.ContainerMutation + */ + + /** + * @TODO check again this file after leaflet v1.9.3 (eg. L.Browser.mutation). + * Mutation Observer support will likely be added by default in next releases. + */ + + L.Map.mergeOptions({ + + /** + * Whether the map uses mutation observers to + * detect changes in its container and trigger + * `invalidateSize`. Disabled by default due to + * support not being available in all web browsers. + * + * @type {Boolean} + * + * @see https://developer.mozilla.org/docs/Web/API/MutationObserver + */ + trackContainerMutation: false + + }); + + L.Map.ContainerMutation = L.Handler.extend({ + + addHooks: function() { + // if (!L.Browser.mutation) { return; } + if (!this._observer) { + this._observer = new MutationObserver(L.Util.bind(this._map.invalidateSize, this._map)); + } + this._observer.observe(this._map.getContainer(), { + childList: false, + attributes: true, + characterData: false, + subtree: false, + attributeFilter: ['style'] + }); + }, + + removeHooks: function() { + // if (!L.Browser.mutation) { return; } + this._observer.disconnect(); + }, + + }); + + /** + * Add Container mutation handler to L.Map (disabled unless `trackContainerMutation` is set). + * + * @property {L.Map.ContainerMutation} trackContainerMutation + */ + L.Map.addInitHook('addHandler', 'trackContainerMutation', L.Map.ContainerMutation); + + /** + * TouchGestures is both TouchZoom plus TouchRotate + * + * @see https://github.com/fnicollet/Leaflet/commit/a77af51a6b10f308d1b9a16552091d1d0aee8834 + * @see https://github.com/Leaflet/Leaflet/blob/v1.9.3/src/map/handler/Map.TouchZoom.js + * + * @typedef L.Map.TouchGestures + */ + + L.Map.mergeOptions({ + + /** + * Set it to false if you don't want the map to + * zoom beyond min/max zoom and then bounce back + * when pinch-zooming. + * + * @type {Boolean} + */ + bounceAtZoomLimits: true, + + }); + + L.Map.TouchGestures = L.Handler.extend({ + + initialize: function(map) { + this._map = map; + this.rotate = !!this._map.options.touchRotate; + this.zoom = !!this._map.options.touchZoom; + }, + + addHooks: function() { + L.DomEvent.on(this._map._container, 'touchstart', this._onTouchStart, this); + }, + + removeHooks: function() { + L.DomEvent.off(this._map._container, 'touchstart', this._onTouchStart, this); + }, + + _onTouchStart: function(e) { + var map = this._map; + + if (!e.touches || e.touches.length !== 2 || map._animatingZoom || this._zooming || this._rotating) { return; } + + var p1 = map.mouseEventToContainerPoint(e.touches[0]), + p2 = map.mouseEventToContainerPoint(e.touches[1]), + vector = p1.subtract(p2); + + this._centerPoint = map.getSize()._divideBy(2); + this._startLatLng = map.containerPointToLatLng(this._centerPoint); + + if (this.zoom) { + if (map.options.touchZoom !== 'center') { + this._pinchStartLatLng = map.containerPointToLatLng(p1.add(p2)._divideBy(2)); + } + this._startDist = p1.distanceTo(p2); + this._startZoom = map.getZoom(); + this._zooming = true; + } else { + this._zooming = false; + } + + if (this.rotate) { + this._startTheta = Math.atan(vector.x / vector.y); + this._startBearing = map.getBearing(); + if (vector.y < 0) { this._startBearing += 180; } + this._rotating = true; + } else { + this._rotating = false; + } + + this._moved = false; + + map._stop(); + + L.DomEvent + .on(document, 'touchmove', this._onTouchMove, this) + .on(document, 'touchend touchcancel', this._onTouchEnd, this); + + L.DomEvent.preventDefault(e); + }, + + _onTouchMove: function(e) { + if (!e.touches || e.touches.length !== 2 || !(this._zooming || this._rotating)) { return; } + + var map = this._map, + p1 = map.mouseEventToContainerPoint(e.touches[0]), + p2 = map.mouseEventToContainerPoint(e.touches[1]), + vector = p1.subtract(p2), + scale = p1.distanceTo(p2) / this._startDist, + delta; + + if (this._rotating) { + var theta = Math.atan(vector.x / vector.y); + var bearingDelta = (theta - this._startTheta) * L.DomUtil.RAD_TO_DEG; + if (vector.y < 0) { bearingDelta += 180; } + if (bearingDelta) { + /** + * @TODO the pivot should be the last touch point, + * but zoomAnimation manages to overwrite the rotate + * pane position. Maybe related to #3529. + * + * @see https://github.com/Leaflet/Leaflet/pull/3529 + * @see https://github.com/fnicollet/Leaflet/commit/a77af51a6b10f308d1b9a16552091d1d0aee8834 + */ + map.setBearing(this._startBearing - bearingDelta); + } + } + + if (this._zooming) { + this._zoom = map.getScaleZoom(scale, this._startZoom); + + if (!map.options.bounceAtZoomLimits && ( + (this._zoom < map.getMinZoom() && scale < 1) || + (this._zoom > map.getMaxZoom() && scale > 1))) { + this._zoom = map._limitZoom(this._zoom); + } + + if (map.options.touchZoom === 'center') { + this._center = this._startLatLng; + if (scale === 1) { return; } + } else { + // Get delta from pinch to center, so centerLatLng is delta applied to initial pinchLatLng + delta = p1._add(p2)._divideBy(2)._subtract(this._centerPoint); + if (scale === 1 && delta.x === 0 && delta.y === 0) { return; } + + var alpha = -map.getBearing() * L.DomUtil.DEG_TO_RAD; + + this._center = map.unproject(map.project(this._pinchStartLatLng).subtract(delta.rotate(alpha))); + } + + } + + if (!this._moved) { + map._moveStart(true, false); + this._moved = true; + } + + L.Util.cancelAnimFrame(this._animRequest); + + var moveFn = map._move.bind(map, this._center, this._zoom, { pinch: true, round: false }, undefined); + this._animRequest = L.Util.requestAnimFrame(moveFn, this, true); + + L.DomEvent.preventDefault(e); + }, + + _onTouchEnd: function() { + if (!this._moved || !(this._zooming || this._rotating)) { + this._zooming = false; + return; + } + + this._zooming = false; + this._rotating = false; + L.Util.cancelAnimFrame(this._animRequest); + + L.DomEvent + .off(document, 'touchmove', this._onTouchMove, this) + .off(document, 'touchend touchcancel', this._onTouchEnd, this); + + if (this.zoom) { + // Pinch updates GridLayers' levels only when zoomSnap is off, so zoomSnap becomes noUpdate. + if (this._map.options.zoomAnimation) { + this._map._animateZoom(this._center, this._map._limitZoom(this._zoom), true, this._map.options.zoomSnap); + } else { + this._map._resetView(this._center, this._map._limitZoom(this._zoom)); + } + } + }, + + }); + + /** + * Add Touch Gestures handler (enabled unless `touchGestures` is unset). + * + * @property {L.Map.TouchGestures} touchGestures + */ + L.Map.addInitHook('addHandler', 'touchGestures', L.Map.TouchGestures); + + /** + * Rotates the map on two-finger (touch devices). + * + * @typedef L.Map.TouchRotate + */ + + L.Map.mergeOptions({ + + /** + * Whether the map can be rotated with a two-finger rotation gesture + * + * @type {Boolean} + */ + touchRotate: false, + + }); + + L.Map.TouchRotate = L.Handler.extend({ + + addHooks: function() { + this._map.touchGestures.enable(); + this._map.touchGestures.rotate = true; + }, + + removeHooks: function() { + this._map.touchGestures.rotate = false; + }, + + }); + + /** + * Add Touch Rotate handler (disabled unless `touchGestures` is set). + * + * @property {L.Map.TouchGestures} touchGestures + */ + L.Map.addInitHook('addHandler', 'touchRotate', L.Map.TouchRotate); + + /** + * Rotates the map on shift key + mousewheel scrolling (desktop). + * + * @typedef L.Map.ShiftKeyRotate + */ + + L.Map.mergeOptions({ + + /** + * Whether the map can be rotated with shift + wheel scroll + * @type {Boolean} + */ + shiftKeyRotate: true, + + }); + + L.Map.ShiftKeyRotate = L.Handler.extend({ + + addHooks: function() { + L.DomEvent.on(this._map._container, "wheel", this._handleShiftScroll, this); + // this._map.shiftKeyRotate.enable(); + this._map.shiftKeyRotate.rotate = true; + }, + + removeHooks: function() { + L.DomEvent.off(this._map._container, "wheel", this._handleShiftScroll, this); + this._map.shiftKeyRotate.rotate = false; + }, + + _handleShiftScroll: function(e) { + if (e.shiftKey) { + e.preventDefault(); + this._map.scrollWheelZoom.disable(); + this._map.setBearing((this._map._bearing * L.DomUtil.RAD_TO_DEG) + Math.sign(e.deltaY) * 5); + } else { + this._map.scrollWheelZoom.enable(); + } + }, + + }); + + /** + * Add ShiftKey handler to L.Map (enabled unless `shiftKeyRotate` is unset). + * + * @property {L.Map.ShiftKeyRotate} shiftKeyRotate + */ + L.Map.addInitHook('addHandler', 'shiftKeyRotate', L.Map.ShiftKeyRotate); + + // decrease `scrollWheelZoom` handler priority over `shiftKeyRotate` handler + L.Map.addInitHook(function() { + if (this.scrollWheelZoom.enabled() && this.shiftKeyRotate.enabled()) { + this.scrollWheelZoom.disable(); + this.scrollWheelZoom.enable(); + } + }); + + /** + * Adds pinch zoom rotation on mobile browsers + * + * @see https://github.com/Leaflet/Leaflet/blob/v1.9.3/src/map/handler/Map.TouchZoom.js + * + * @external L.Map.TouchZoom + */ + + L.Map.mergeOptions({ + + /** + * Whether the map can be zoomed by touch-dragging + * with two fingers. If passed `'center'`, it will + * zoom to the center of the view regardless of + * where the touch events (fingers) were. Enabled + * for touch-capable web browsers. + * + * @type {(Boolean|String)} + */ + touchZoom: L.Browser.touch, + + /** + * @TODO check if this is a duplicate of `L.Map.TouchGestures::bounceAtZoomLimits` + * + * Set it to false if you don't want the map to + * zoom beyond min/max zoom and then bounce back + * when pinch-zooming. + * + * @type {Boolean} + */ + bounceAtZoomLimits: false, + + }); + + L.Map.TouchZoom = L.Handler.extend({ + + addHooks: function() { + L.DomUtil.addClass(this._map._container, 'leaflet-touch-zoom'); + this._map.touchGestures.enable(); + this._map.touchGestures.zoom = true; + }, + + removeHooks: function() { + L.DomUtil.removeClass(this._map._container, 'leaflet-touch-zoom'); + this._map.touchGestures.zoom = false; + }, + + }); + + /** + * Add Touch Zoom handler (disabled unless `L.Browser.touch` is set). + * + * @property {L.Map.TouchGestures} touchGestures + */ + L.Map.addInitHook('addHandler', 'touchZoom', L.Map.TouchZoom); + + /** + * A tri-state control for map rotation, states are: + * + * - Locked (default) + * - Unlocked (user can pinch-rotate) + * - Follow (rotation follows device orientation, if available) + * + * @typedef L.Control.Rotate + */ + + L.Control.Rotate = L.Control.extend({ + + options: { + position: 'topleft', + closeOnZeroBearing: true + }, + + onAdd: function(map) { + var container = this._container = L.DomUtil.create('div', 'leaflet-control-rotate leaflet-bar'); + + // this.button = L.Control.Zoom.prototype._createButton.call(this, 'R', 'leaflet-control-rotate', 'leaflet-control-rotate', container, this._toggleLock); + + var arrow = this._arrow = L.DomUtil.create('span', 'leaflet-control-rotate-arrow'); + + arrow.style.backgroundImage = `url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 29 29' xmlns='http://www.w3.org/2000/svg' fill='%23333'%3E%3Cpath d='M10.5 14l4-8 4 8h-8z'/%3E%3Cpath d='M10.5 16l4 8 4-8h-8z' fill='%23ccc'/%3E%3C/svg%3E")`; + arrow.style.cursor = 'grab'; + arrow.style.display = 'block'; + arrow.style.width = '100%'; + arrow.style.height = '100%'; + arrow.style.backgroundRepeat = 'no-repeat'; + arrow.style.backgroundPosition = '50%'; + + // Copy-pasted from L.Control.Zoom + var link = this._link = L.DomUtil.create('a', 'leaflet-control-rotate-toggle', container); + link.appendChild(arrow); + link.href = '#'; + link.title = 'Rotate map'; + + L.DomEvent + .on(link, 'dblclick', L.DomEvent.stopPropagation) + .on(link, 'mousedown', this._handleMouseDown, this) + .on(link, 'click', L.DomEvent.stop) + .on(link, 'click', this._cycleState, this) + .on(link, 'click', this._refocusOnMap, this); + + if (!L.Browser.any3d) { + L.DomUtil.addClass(link, 'leaflet-disabled'); + } + + this._restyle(); + + map.on('rotate', this._restyle, this); + + // State flag + this._follow = false; + this._canFollow = false; + + if (this.options.closeOnZeroBearing && map.getBearing() === 0) { + container.style.display = 'none'; + } + + return container; + }, + + onRemove: function(map) { + map.off('rotate', this._restyle, this); + }, + + _handleMouseDown: function(e) { + L.DomEvent.stop(e); + this.dragging = true; + this.dragstartX = e.pageX; + this.dragstartY = e.pageY; + L.DomEvent + .on(document, 'mousemove', this._handleMouseDrag, this) + .on(document, 'mouseup', this._handleMouseUp, this); + }, + + _handleMouseUp: function(e) { + L.DomEvent.stop(e); + this.dragging = false; + + L.DomEvent + .off(document, 'mousemove', this._handleMouseDrag, this) + .off(document, 'mouseup', this._handleMouseUp, this); + }, + + _handleMouseDrag: function(e) { + if (!this.dragging) { return; } + var deltaX = e.clientX - this.dragstartX; + this._map.setBearing(deltaX); + }, + + _cycleState: function(ev) { + if (!this._map) { + return; + } + + var map = this._map; + + // Touch mode + if (!map.touchRotate.enabled() && !map.compassBearing.enabled()) { + map.touchRotate.enable(); + } + + // Compass mode + else if (!map.compassBearing.enabled()) { + map.touchRotate.disable(); + ( + DeviceOrientationEvent && DeviceOrientationEvent.requestPermission + ? DeviceOrientationEvent.requestPermission() // iOS compass + : Promise.resolve('granted') // others + ).then(state => "granted" === state && map.compassBearing.enable()); + } + + // Locked mode + else { + map.compassBearing.disable(); + map.setBearing(0); + if (this.options.closeOnZeroBearing) { + map.touchRotate.enable(); + } + } + this._restyle(); + }, + + _restyle: function() { + if (!this._map.options.rotate) { + L.DomUtil.addClass(this._link, 'leaflet-disabled'); + } else { + var map = this._map; + var bearing = map.getBearing(); + + this._arrow.style.transform = 'rotate(' + bearing + 'deg)'; + + if (bearing && this.options.closeOnZeroBearing) { + this._container.style.display = 'block'; + } + + // Compass mode + if (map.compassBearing.enabled()) { + this._link.style.backgroundColor = 'orange'; + } + + // Touch mode + else if (map.touchRotate.enabled()) { + this._link.style.backgroundColor = null; + } + + // Locked mode + else { + this._link.style.backgroundColor = 'grey'; + if (0 === bearing && this.options.closeOnZeroBearing) { + this._container.style.display = 'none'; + } + } + } + }, + + }); + + L.control.rotate = function(options) { + return new L.Control.Rotate(options); + }; + + L.Map.mergeOptions({ + rotateControl: true, + }); + + L.Map.addInitHook(function() { + if (this.options.rotateControl) { + var options = typeof this.options.rotateControl === 'object' ? this.options.rotateControl : {}; + this.rotateControl = L.control.rotate(options); + this.addControl(this.rotateControl); + } + }); + +})); +//# sourceMappingURL=leaflet-rotate-src.js.map diff --git a/static/leaflet/leaflet.css b/static/leaflet/leaflet.css new file mode 100644 index 0000000..9ade8dc --- /dev/null +++ b/static/leaflet/leaflet.css @@ -0,0 +1,661 @@ +/* required styles */ + +.leaflet-pane, +.leaflet-tile, +.leaflet-marker-icon, +.leaflet-marker-shadow, +.leaflet-tile-container, +.leaflet-pane > svg, +.leaflet-pane > canvas, +.leaflet-zoom-box, +.leaflet-image-layer, +.leaflet-layer { + position: absolute; + left: 0; + top: 0; + } +.leaflet-container { + overflow: hidden; + } +.leaflet-tile, +.leaflet-marker-icon, +.leaflet-marker-shadow { + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + -webkit-user-drag: none; + } +/* Prevents IE11 from highlighting tiles in blue */ +.leaflet-tile::selection { + background: transparent; +} +/* Safari renders non-retina tile on retina better with this, but Chrome is worse */ +.leaflet-safari .leaflet-tile { + image-rendering: -webkit-optimize-contrast; + } +/* hack that prevents hw layers "stretching" when loading new tiles */ +.leaflet-safari .leaflet-tile-container { + width: 1600px; + height: 1600px; + -webkit-transform-origin: 0 0; + } +.leaflet-marker-icon, +.leaflet-marker-shadow { + display: block; + } +/* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */ +/* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */ +.leaflet-container .leaflet-overlay-pane svg { + max-width: none !important; + max-height: none !important; + } +.leaflet-container .leaflet-marker-pane img, +.leaflet-container .leaflet-shadow-pane img, +.leaflet-container .leaflet-tile-pane img, +.leaflet-container img.leaflet-image-layer, +.leaflet-container .leaflet-tile { + max-width: none !important; + max-height: none !important; + width: auto; + padding: 0; + } + +.leaflet-container img.leaflet-tile { + /* See: https://bugs.chromium.org/p/chromium/issues/detail?id=600120 */ + mix-blend-mode: plus-lighter; +} + +.leaflet-container.leaflet-touch-zoom { + -ms-touch-action: pan-x pan-y; + touch-action: pan-x pan-y; + } +.leaflet-container.leaflet-touch-drag { + -ms-touch-action: pinch-zoom; + /* Fallback for FF which doesn't support pinch-zoom */ + touch-action: none; + touch-action: pinch-zoom; +} +.leaflet-container.leaflet-touch-drag.leaflet-touch-zoom { + -ms-touch-action: none; + touch-action: none; +} +.leaflet-container { + -webkit-tap-highlight-color: transparent; +} +.leaflet-container a { + -webkit-tap-highlight-color: rgba(51, 181, 229, 0.4); +} +.leaflet-tile { + filter: inherit; + visibility: hidden; + } +.leaflet-tile-loaded { + visibility: inherit; + } +.leaflet-zoom-box { + width: 0; + height: 0; + -moz-box-sizing: border-box; + box-sizing: border-box; + z-index: 800; + } +/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */ +.leaflet-overlay-pane svg { + -moz-user-select: none; + } + +.leaflet-pane { z-index: 400; } + +.leaflet-tile-pane { z-index: 200; } +.leaflet-overlay-pane { z-index: 400; } +.leaflet-shadow-pane { z-index: 500; } +.leaflet-marker-pane { z-index: 600; } +.leaflet-tooltip-pane { z-index: 650; } +.leaflet-popup-pane { z-index: 700; } + +.leaflet-map-pane canvas { z-index: 100; } +.leaflet-map-pane svg { z-index: 200; } + +.leaflet-vml-shape { + width: 1px; + height: 1px; + } +.lvml { + behavior: url(#default#VML); + display: inline-block; + position: absolute; + } + + +/* control positioning */ + +.leaflet-control { + position: relative; + z-index: 800; + pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */ + pointer-events: auto; + } +.leaflet-top, +.leaflet-bottom { + position: absolute; + z-index: 1000; + pointer-events: none; + } +.leaflet-top { + top: 0; + } +.leaflet-right { + right: 0; + } +.leaflet-bottom { + bottom: 0; + } +.leaflet-left { + left: 0; + } +.leaflet-control { + float: left; + clear: both; + } +.leaflet-right .leaflet-control { + float: right; + } +.leaflet-top .leaflet-control { + margin-top: 10px; + } +.leaflet-bottom .leaflet-control { + margin-bottom: 10px; + } +.leaflet-left .leaflet-control { + margin-left: 10px; + } +.leaflet-right .leaflet-control { + margin-right: 10px; + } + + +/* zoom and fade animations */ + +.leaflet-fade-anim .leaflet-popup { + opacity: 0; + -webkit-transition: opacity 0.2s linear; + -moz-transition: opacity 0.2s linear; + transition: opacity 0.2s linear; + } +.leaflet-fade-anim .leaflet-map-pane .leaflet-popup { + opacity: 1; + } +.leaflet-zoom-animated { + -webkit-transform-origin: 0 0; + -ms-transform-origin: 0 0; + transform-origin: 0 0; + } +svg.leaflet-zoom-animated { + will-change: transform; +} + +.leaflet-zoom-anim .leaflet-zoom-animated { + -webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1); + -moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1); + transition: transform 0.25s cubic-bezier(0,0,0.25,1); + } +.leaflet-zoom-anim .leaflet-tile, +.leaflet-pan-anim .leaflet-tile { + -webkit-transition: none; + -moz-transition: none; + transition: none; + } + +.leaflet-zoom-anim .leaflet-zoom-hide { + visibility: hidden; + } + + +/* cursors */ + +.leaflet-interactive { + cursor: pointer; + } +.leaflet-grab { + cursor: -webkit-grab; + cursor: -moz-grab; + cursor: grab; + } +.leaflet-crosshair, +.leaflet-crosshair .leaflet-interactive { + cursor: crosshair; + } +.leaflet-popup-pane, +.leaflet-control { + cursor: auto; + } +.leaflet-dragging .leaflet-grab, +.leaflet-dragging .leaflet-grab .leaflet-interactive, +.leaflet-dragging .leaflet-marker-draggable { + cursor: move; + cursor: -webkit-grabbing; + cursor: -moz-grabbing; + cursor: grabbing; + } + +/* marker & overlays interactivity */ +.leaflet-marker-icon, +.leaflet-marker-shadow, +.leaflet-image-layer, +.leaflet-pane > svg path, +.leaflet-tile-container { + pointer-events: none; + } + +.leaflet-marker-icon.leaflet-interactive, +.leaflet-image-layer.leaflet-interactive, +.leaflet-pane > svg path.leaflet-interactive, +svg.leaflet-image-layer.leaflet-interactive path { + pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */ + pointer-events: auto; + } + +/* visual tweaks */ + +.leaflet-container { + background: #ddd; + outline-offset: 1px; + } +.leaflet-container a { + color: #0078A8; + } +.leaflet-zoom-box { + border: 2px dotted #38f; + background: rgba(255,255,255,0.5); + } + + +/* general typography */ +.leaflet-container { + font-family: "Helvetica Neue", Arial, Helvetica, sans-serif; + font-size: 12px; + font-size: 0.75rem; + line-height: 1.5; + } + + +/* general toolbar styles */ + +.leaflet-bar { + box-shadow: 0 1px 5px rgba(0,0,0,0.65); + border-radius: 4px; + } +.leaflet-bar a { + background-color: #fff; + border-bottom: 1px solid #ccc; + width: 26px; + height: 26px; + line-height: 26px; + display: block; + text-align: center; + text-decoration: none; + color: black; + } +.leaflet-bar a, +.leaflet-control-layers-toggle { + background-position: 50% 50%; + background-repeat: no-repeat; + display: block; + } +.leaflet-bar a:hover, +.leaflet-bar a:focus { + background-color: #f4f4f4; + } +.leaflet-bar a:first-child { + border-top-left-radius: 4px; + border-top-right-radius: 4px; + } +.leaflet-bar a:last-child { + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + border-bottom: none; + } +.leaflet-bar a.leaflet-disabled { + cursor: default; + background-color: #f4f4f4; + color: #bbb; + } + +.leaflet-touch .leaflet-bar a { + width: 30px; + height: 30px; + line-height: 30px; + } +.leaflet-touch .leaflet-bar a:first-child { + border-top-left-radius: 2px; + border-top-right-radius: 2px; + } +.leaflet-touch .leaflet-bar a:last-child { + border-bottom-left-radius: 2px; + border-bottom-right-radius: 2px; + } + +/* zoom control */ + +.leaflet-control-zoom-in, +.leaflet-control-zoom-out { + font: bold 18px 'Lucida Console', Monaco, monospace; + text-indent: 1px; + } + +.leaflet-touch .leaflet-control-zoom-in, .leaflet-touch .leaflet-control-zoom-out { + font-size: 22px; + } + + +/* layers control */ + +.leaflet-control-layers { + box-shadow: 0 1px 5px rgba(0,0,0,0.4); + background: #fff; + border-radius: 5px; + } +.leaflet-control-layers-toggle { + background-image: url(images/layers.png); + width: 36px; + height: 36px; + } +.leaflet-retina .leaflet-control-layers-toggle { + background-image: url(images/layers-2x.png); + background-size: 26px 26px; + } +.leaflet-touch .leaflet-control-layers-toggle { + width: 44px; + height: 44px; + } +.leaflet-control-layers .leaflet-control-layers-list, +.leaflet-control-layers-expanded .leaflet-control-layers-toggle { + display: none; + } +.leaflet-control-layers-expanded .leaflet-control-layers-list { + display: block; + position: relative; + } +.leaflet-control-layers-expanded { + padding: 6px 10px 6px 6px; + color: #333; + background: #fff; + } +.leaflet-control-layers-scrollbar { + overflow-y: scroll; + overflow-x: hidden; + padding-right: 5px; + } +.leaflet-control-layers-selector { + margin-top: 2px; + position: relative; + top: 1px; + } +.leaflet-control-layers label { + display: block; + font-size: 13px; + font-size: 1.08333em; + } +.leaflet-control-layers-separator { + height: 0; + border-top: 1px solid #ddd; + margin: 5px -10px 5px -6px; + } + +/* Default icon URLs */ +.leaflet-default-icon-path { /* used only in path-guessing heuristic, see L.Icon.Default */ + background-image: url(images/marker-icon.png); + } + + +/* attribution and scale controls */ + +.leaflet-container .leaflet-control-attribution { + background: #fff; + background: rgba(255, 255, 255, 0.8); + margin: 0; + } +.leaflet-control-attribution, +.leaflet-control-scale-line { + padding: 0 5px; + color: #333; + line-height: 1.4; + } +.leaflet-control-attribution a { + text-decoration: none; + } +.leaflet-control-attribution a:hover, +.leaflet-control-attribution a:focus { + text-decoration: underline; + } +.leaflet-attribution-flag { + display: inline !important; + vertical-align: baseline !important; + width: 1em; + height: 0.6669em; + } +.leaflet-left .leaflet-control-scale { + margin-left: 5px; + } +.leaflet-bottom .leaflet-control-scale { + margin-bottom: 5px; + } +.leaflet-control-scale-line { + border: 2px solid #777; + border-top: none; + line-height: 1.1; + padding: 2px 5px 1px; + white-space: nowrap; + -moz-box-sizing: border-box; + box-sizing: border-box; + background: rgba(255, 255, 255, 0.8); + text-shadow: 1px 1px #fff; + } +.leaflet-control-scale-line:not(:first-child) { + border-top: 2px solid #777; + border-bottom: none; + margin-top: -2px; + } +.leaflet-control-scale-line:not(:first-child):not(:last-child) { + border-bottom: 2px solid #777; + } + +.leaflet-touch .leaflet-control-attribution, +.leaflet-touch .leaflet-control-layers, +.leaflet-touch .leaflet-bar { + box-shadow: none; + } +.leaflet-touch .leaflet-control-layers, +.leaflet-touch .leaflet-bar { + border: 2px solid rgba(0,0,0,0.2); + background-clip: padding-box; + } + + +/* popup */ + +.leaflet-popup { + position: absolute; + text-align: center; + margin-bottom: 20px; + } +.leaflet-popup-content-wrapper { + padding: 1px; + text-align: left; + border-radius: 12px; + } +.leaflet-popup-content { + margin: 13px 24px 13px 20px; + line-height: 1.3; + font-size: 13px; + font-size: 1.08333em; + min-height: 1px; + } +.leaflet-popup-content p { + margin: 17px 0; + margin: 1.3em 0; + } +.leaflet-popup-tip-container { + width: 40px; + height: 20px; + position: absolute; + left: 50%; + margin-top: -1px; + margin-left: -20px; + overflow: hidden; + pointer-events: none; + } +.leaflet-popup-tip { + width: 17px; + height: 17px; + padding: 1px; + + margin: -10px auto 0; + pointer-events: auto; + + -webkit-transform: rotate(45deg); + -moz-transform: rotate(45deg); + -ms-transform: rotate(45deg); + transform: rotate(45deg); + } +.leaflet-popup-content-wrapper, +.leaflet-popup-tip { + background: white; + color: #333; + box-shadow: 0 3px 14px rgba(0,0,0,0.4); + } +.leaflet-container a.leaflet-popup-close-button { + position: absolute; + top: 0; + right: 0; + border: none; + text-align: center; + width: 24px; + height: 24px; + font: 16px/24px Tahoma, Verdana, sans-serif; + color: #757575; + text-decoration: none; + background: transparent; + } +.leaflet-container a.leaflet-popup-close-button:hover, +.leaflet-container a.leaflet-popup-close-button:focus { + color: #585858; + } +.leaflet-popup-scrolled { + overflow: auto; + } + +.leaflet-oldie .leaflet-popup-content-wrapper { + -ms-zoom: 1; + } +.leaflet-oldie .leaflet-popup-tip { + width: 24px; + margin: 0 auto; + + -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)"; + filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678); + } + +.leaflet-oldie .leaflet-control-zoom, +.leaflet-oldie .leaflet-control-layers, +.leaflet-oldie .leaflet-popup-content-wrapper, +.leaflet-oldie .leaflet-popup-tip { + border: 1px solid #999; + } + + +/* div icon */ + +.leaflet-div-icon { + background: #fff; + border: 1px solid #666; + } + + +/* Tooltip */ +/* Base styles for the element that has a tooltip */ +.leaflet-tooltip { + position: absolute; + padding: 6px; + background-color: #fff; + border: 1px solid #fff; + border-radius: 3px; + color: #222; + white-space: nowrap; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + pointer-events: none; + box-shadow: 0 1px 3px rgba(0,0,0,0.4); + } +.leaflet-tooltip.leaflet-interactive { + cursor: pointer; + pointer-events: auto; + } +.leaflet-tooltip-top:before, +.leaflet-tooltip-bottom:before, +.leaflet-tooltip-left:before, +.leaflet-tooltip-right:before { + position: absolute; + pointer-events: none; + border: 6px solid transparent; + background: transparent; + content: ""; + } + +/* Directions */ + +.leaflet-tooltip-bottom { + margin-top: 6px; +} +.leaflet-tooltip-top { + margin-top: -6px; +} +.leaflet-tooltip-bottom:before, +.leaflet-tooltip-top:before { + left: 50%; + margin-left: -6px; + } +.leaflet-tooltip-top:before { + bottom: 0; + margin-bottom: -12px; + border-top-color: #fff; + } +.leaflet-tooltip-bottom:before { + top: 0; + margin-top: -12px; + margin-left: -6px; + border-bottom-color: #fff; + } +.leaflet-tooltip-left { + margin-left: -6px; +} +.leaflet-tooltip-right { + margin-left: 6px; +} +.leaflet-tooltip-left:before, +.leaflet-tooltip-right:before { + top: 50%; + margin-top: -6px; + } +.leaflet-tooltip-left:before { + right: 0; + margin-right: -12px; + border-left-color: #fff; + } +.leaflet-tooltip-right:before { + left: 0; + margin-left: -12px; + border-right-color: #fff; + } + +/* Printing */ + +@media print { + /* Prevent printers from removing background-images of controls. */ + .leaflet-control { + -webkit-print-color-adjust: exact; + print-color-adjust: exact; + } + } diff --git a/static/leaflet/leaflet.js b/static/leaflet/leaflet.js new file mode 100644 index 0000000..1a3c8ed --- /dev/null +++ b/static/leaflet/leaflet.js @@ -0,0 +1,5 @@ +/* @preserve + * Leaflet 1.9.4, a JS library for interactive maps. https://leafletjs.com + * (c) 2010-2023 Vladimir Agafonkin, (c) 2010-2011 CloudMade + */ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).leaflet={})}(this,function(t){"use strict";function l(t){for(var e,i,n=1,o=arguments.length;n=this.min.x&&i.x<=this.max.x&&e.y>=this.min.y&&i.y<=this.max.y},intersects:function(t){t=_(t);var e=this.min,i=this.max,n=t.min,t=t.max,o=t.x>=e.x&&n.x<=i.x,t=t.y>=e.y&&n.y<=i.y;return o&&t},overlaps:function(t){t=_(t);var e=this.min,i=this.max,n=t.min,t=t.max,o=t.x>e.x&&n.xe.y&&n.y=n.lat&&i.lat<=o.lat&&e.lng>=n.lng&&i.lng<=o.lng},intersects:function(t){t=g(t);var e=this._southWest,i=this._northEast,n=t.getSouthWest(),t=t.getNorthEast(),o=t.lat>=e.lat&&n.lat<=i.lat,t=t.lng>=e.lng&&n.lng<=i.lng;return o&&t},overlaps:function(t){t=g(t);var e=this._southWest,i=this._northEast,n=t.getSouthWest(),t=t.getNorthEast(),o=t.lat>e.lat&&n.late.lng&&n.lng","http://www.w3.org/2000/svg"===(Wt.firstChild&&Wt.firstChild.namespaceURI));function y(t){return 0<=navigator.userAgent.toLowerCase().indexOf(t)}var b={ie:pt,ielt9:mt,edge:n,webkit:ft,android:gt,android23:vt,androidStock:yt,opera:xt,chrome:wt,gecko:bt,safari:Pt,phantom:Lt,opera12:o,win:Tt,ie3d:Mt,webkit3d:zt,gecko3d:_t,any3d:Ct,mobile:Zt,mobileWebkit:St,mobileWebkit3d:Et,msPointer:kt,pointer:Ot,touch:Bt,touchNative:At,mobileOpera:It,mobileGecko:Rt,retina:Nt,passiveEvents:Dt,canvas:jt,svg:Ht,vml:!Ht&&function(){try{var t=document.createElement("div"),e=(t.innerHTML='',t.firstChild);return e.style.behavior="url(#default#VML)",e&&"object"==typeof e.adj}catch(t){return!1}}(),inlineSvg:Wt,mac:0===navigator.platform.indexOf("Mac"),linux:0===navigator.platform.indexOf("Linux")},Ft=b.msPointer?"MSPointerDown":"pointerdown",Ut=b.msPointer?"MSPointerMove":"pointermove",Vt=b.msPointer?"MSPointerUp":"pointerup",qt=b.msPointer?"MSPointerCancel":"pointercancel",Gt={touchstart:Ft,touchmove:Ut,touchend:Vt,touchcancel:qt},Kt={touchstart:function(t,e){e.MSPOINTER_TYPE_TOUCH&&e.pointerType===e.MSPOINTER_TYPE_TOUCH&&O(e);ee(t,e)},touchmove:ee,touchend:ee,touchcancel:ee},Yt={},Xt=!1;function Jt(t,e,i){return"touchstart"!==e||Xt||(document.addEventListener(Ft,$t,!0),document.addEventListener(Ut,Qt,!0),document.addEventListener(Vt,te,!0),document.addEventListener(qt,te,!0),Xt=!0),Kt[e]?(i=Kt[e].bind(this,i),t.addEventListener(Gt[e],i,!1),i):(console.warn("wrong event specified:",e),u)}function $t(t){Yt[t.pointerId]=t}function Qt(t){Yt[t.pointerId]&&(Yt[t.pointerId]=t)}function te(t){delete Yt[t.pointerId]}function ee(t,e){if(e.pointerType!==(e.MSPOINTER_TYPE_MOUSE||"mouse")){for(var i in e.touches=[],Yt)e.touches.push(Yt[i]);e.changedTouches=[e],t(e)}}var ie=200;function ne(t,i){t.addEventListener("dblclick",i);var n,o=0;function e(t){var e;1!==t.detail?n=t.detail:"mouse"===t.pointerType||t.sourceCapabilities&&!t.sourceCapabilities.firesTouchEvents||((e=Ne(t)).some(function(t){return t instanceof HTMLLabelElement&&t.attributes.for})&&!e.some(function(t){return t instanceof HTMLInputElement||t instanceof HTMLSelectElement})||((e=Date.now())-o<=ie?2===++n&&i(function(t){var e,i,n={};for(i in t)e=t[i],n[i]=e&&e.bind?e.bind(t):e;return(t=n).type="dblclick",n.detail=2,n.isTrusted=!1,n._simulated=!0,n}(t)):n=1,o=e))}return t.addEventListener("click",e),{dblclick:i,simDblclick:e}}var oe,se,re,ae,he,le,ue=we(["transform","webkitTransform","OTransform","MozTransform","msTransform"]),ce=we(["webkitTransition","transition","OTransition","MozTransition","msTransition"]),de="webkitTransition"===ce||"OTransition"===ce?ce+"End":"transitionend";function _e(t){return"string"==typeof t?document.getElementById(t):t}function pe(t,e){var i=t.style[e]||t.currentStyle&&t.currentStyle[e];return"auto"===(i=i&&"auto"!==i||!document.defaultView?i:(t=document.defaultView.getComputedStyle(t,null))?t[e]:null)?null:i}function P(t,e,i){t=document.createElement(t);return t.className=e||"",i&&i.appendChild(t),t}function T(t){var e=t.parentNode;e&&e.removeChild(t)}function me(t){for(;t.firstChild;)t.removeChild(t.firstChild)}function fe(t){var e=t.parentNode;e&&e.lastChild!==t&&e.appendChild(t)}function ge(t){var e=t.parentNode;e&&e.firstChild!==t&&e.insertBefore(t,e.firstChild)}function ve(t,e){return void 0!==t.classList?t.classList.contains(e):0<(t=xe(t)).length&&new RegExp("(^|\\s)"+e+"(\\s|$)").test(t)}function M(t,e){var i;if(void 0!==t.classList)for(var n=F(e),o=0,s=n.length;othis.options.maxZoom)?this.setZoom(t):this},panInsideBounds:function(t,e){this._enforcingBounds=!0;var i=this.getCenter(),t=this._limitCenter(i,this._zoom,g(t));return i.equals(t)||this.panTo(t,e),this._enforcingBounds=!1,this},panInside:function(t,e){var i=m((e=e||{}).paddingTopLeft||e.padding||[0,0]),n=m(e.paddingBottomRight||e.padding||[0,0]),o=this.project(this.getCenter()),t=this.project(t),s=this.getPixelBounds(),i=_([s.min.add(i),s.max.subtract(n)]),s=i.getSize();return i.contains(t)||(this._enforcingBounds=!0,n=t.subtract(i.getCenter()),i=i.extend(t).getSize().subtract(s),o.x+=n.x<0?-i.x:i.x,o.y+=n.y<0?-i.y:i.y,this.panTo(this.unproject(o),e),this._enforcingBounds=!1),this},invalidateSize:function(t){if(!this._loaded)return this;t=l({animate:!1,pan:!0},!0===t?{animate:!0}:t);var e=this.getSize(),i=(this._sizeChanged=!0,this._lastCenter=null,this.getSize()),n=e.divideBy(2).round(),o=i.divideBy(2).round(),n=n.subtract(o);return n.x||n.y?(t.animate&&t.pan?this.panBy(n):(t.pan&&this._rawPanBy(n),this.fire("move"),t.debounceMoveend?(clearTimeout(this._sizeTimer),this._sizeTimer=setTimeout(a(this.fire,this,"moveend"),200)):this.fire("moveend")),this.fire("resize",{oldSize:e,newSize:i})):this},stop:function(){return this.setZoom(this._limitZoom(this._zoom)),this.options.zoomSnap||this.fire("viewreset"),this._stop()},locate:function(t){var e,i;return t=this._locateOptions=l({timeout:1e4,watch:!1},t),"geolocation"in navigator?(e=a(this._handleGeolocationResponse,this),i=a(this._handleGeolocationError,this),t.watch?this._locationWatchId=navigator.geolocation.watchPosition(e,i,t):navigator.geolocation.getCurrentPosition(e,i,t)):this._handleGeolocationError({code:0,message:"Geolocation not supported."}),this},stopLocate:function(){return navigator.geolocation&&navigator.geolocation.clearWatch&&navigator.geolocation.clearWatch(this._locationWatchId),this._locateOptions&&(this._locateOptions.setView=!1),this},_handleGeolocationError:function(t){var e;this._container._leaflet_id&&(e=t.code,t=t.message||(1===e?"permission denied":2===e?"position unavailable":"timeout"),this._locateOptions.setView&&!this._loaded&&this.fitWorld(),this.fire("locationerror",{code:e,message:"Geolocation error: "+t+"."}))},_handleGeolocationResponse:function(t){if(this._container._leaflet_id){var e,i,n=new v(t.coords.latitude,t.coords.longitude),o=n.toBounds(2*t.coords.accuracy),s=this._locateOptions,r=(s.setView&&(e=this.getBoundsZoom(o),this.setView(n,s.maxZoom?Math.min(e,s.maxZoom):e)),{latlng:n,bounds:o,timestamp:t.timestamp});for(i in t.coords)"number"==typeof t.coords[i]&&(r[i]=t.coords[i]);this.fire("locationfound",r)}},addHandler:function(t,e){return e&&(e=this[t]=new e(this),this._handlers.push(e),this.options[t]&&e.enable()),this},remove:function(){if(this._initEvents(!0),this.options.maxBounds&&this.off("moveend",this._panInsideMaxBounds),this._containerId!==this._container._leaflet_id)throw new Error("Map container is being reused by another instance");try{delete this._container._leaflet_id,delete this._containerId}catch(t){this._container._leaflet_id=void 0,this._containerId=void 0}for(var t in void 0!==this._locationWatchId&&this.stopLocate(),this._stop(),T(this._mapPane),this._clearControlPos&&this._clearControlPos(),this._resizeRequest&&(r(this._resizeRequest),this._resizeRequest=null),this._clearHandlers(),this._loaded&&this.fire("unload"),this._layers)this._layers[t].remove();for(t in this._panes)T(this._panes[t]);return this._layers=[],this._panes=[],delete this._mapPane,delete this._renderer,this},createPane:function(t,e){e=P("div","leaflet-pane"+(t?" leaflet-"+t.replace("Pane","")+"-pane":""),e||this._mapPane);return t&&(this._panes[t]=e),e},getCenter:function(){return this._checkIfLoaded(),this._lastCenter&&!this._moved()?this._lastCenter.clone():this.layerPointToLatLng(this._getCenterLayerPoint())},getZoom:function(){return this._zoom},getBounds:function(){var t=this.getPixelBounds();return new s(this.unproject(t.getBottomLeft()),this.unproject(t.getTopRight()))},getMinZoom:function(){return void 0===this.options.minZoom?this._layersMinZoom||0:this.options.minZoom},getMaxZoom:function(){return void 0===this.options.maxZoom?void 0===this._layersMaxZoom?1/0:this._layersMaxZoom:this.options.maxZoom},getBoundsZoom:function(t,e,i){t=g(t),i=m(i||[0,0]);var n=this.getZoom()||0,o=this.getMinZoom(),s=this.getMaxZoom(),r=t.getNorthWest(),t=t.getSouthEast(),i=this.getSize().subtract(i),t=_(this.project(t,n),this.project(r,n)).getSize(),r=b.any3d?this.options.zoomSnap:1,a=i.x/t.x,i=i.y/t.y,t=e?Math.max(a,i):Math.min(a,i),n=this.getScaleZoom(t,n);return r&&(n=Math.round(n/(r/100))*(r/100),n=e?Math.ceil(n/r)*r:Math.floor(n/r)*r),Math.max(o,Math.min(s,n))},getSize:function(){return this._size&&!this._sizeChanged||(this._size=new p(this._container.clientWidth||0,this._container.clientHeight||0),this._sizeChanged=!1),this._size.clone()},getPixelBounds:function(t,e){t=this._getTopLeftPoint(t,e);return new f(t,t.add(this.getSize()))},getPixelOrigin:function(){return this._checkIfLoaded(),this._pixelOrigin},getPixelWorldBounds:function(t){return this.options.crs.getProjectedBounds(void 0===t?this.getZoom():t)},getPane:function(t){return"string"==typeof t?this._panes[t]:t},getPanes:function(){return this._panes},getContainer:function(){return this._container},getZoomScale:function(t,e){var i=this.options.crs;return e=void 0===e?this._zoom:e,i.scale(t)/i.scale(e)},getScaleZoom:function(t,e){var i=this.options.crs,t=(e=void 0===e?this._zoom:e,i.zoom(t*i.scale(e)));return isNaN(t)?1/0:t},project:function(t,e){return e=void 0===e?this._zoom:e,this.options.crs.latLngToPoint(w(t),e)},unproject:function(t,e){return e=void 0===e?this._zoom:e,this.options.crs.pointToLatLng(m(t),e)},layerPointToLatLng:function(t){t=m(t).add(this.getPixelOrigin());return this.unproject(t)},latLngToLayerPoint:function(t){return this.project(w(t))._round()._subtract(this.getPixelOrigin())},wrapLatLng:function(t){return this.options.crs.wrapLatLng(w(t))},wrapLatLngBounds:function(t){return this.options.crs.wrapLatLngBounds(g(t))},distance:function(t,e){return this.options.crs.distance(w(t),w(e))},containerPointToLayerPoint:function(t){return m(t).subtract(this._getMapPanePos())},layerPointToContainerPoint:function(t){return m(t).add(this._getMapPanePos())},containerPointToLatLng:function(t){t=this.containerPointToLayerPoint(m(t));return this.layerPointToLatLng(t)},latLngToContainerPoint:function(t){return this.layerPointToContainerPoint(this.latLngToLayerPoint(w(t)))},mouseEventToContainerPoint:function(t){return De(t,this._container)},mouseEventToLayerPoint:function(t){return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(t))},mouseEventToLatLng:function(t){return this.layerPointToLatLng(this.mouseEventToLayerPoint(t))},_initContainer:function(t){t=this._container=_e(t);if(!t)throw new Error("Map container not found.");if(t._leaflet_id)throw new Error("Map container is already initialized.");S(t,"scroll",this._onScroll,this),this._containerId=h(t)},_initLayout:function(){var t=this._container,e=(this._fadeAnimated=this.options.fadeAnimation&&b.any3d,M(t,"leaflet-container"+(b.touch?" leaflet-touch":"")+(b.retina?" leaflet-retina":"")+(b.ielt9?" leaflet-oldie":"")+(b.safari?" leaflet-safari":"")+(this._fadeAnimated?" leaflet-fade-anim":"")),pe(t,"position"));"absolute"!==e&&"relative"!==e&&"fixed"!==e&&"sticky"!==e&&(t.style.position="relative"),this._initPanes(),this._initControlPos&&this._initControlPos()},_initPanes:function(){var t=this._panes={};this._paneRenderers={},this._mapPane=this.createPane("mapPane",this._container),Z(this._mapPane,new p(0,0)),this.createPane("tilePane"),this.createPane("overlayPane"),this.createPane("shadowPane"),this.createPane("markerPane"),this.createPane("tooltipPane"),this.createPane("popupPane"),this.options.markerZoomAnimation||(M(t.markerPane,"leaflet-zoom-hide"),M(t.shadowPane,"leaflet-zoom-hide"))},_resetView:function(t,e,i){Z(this._mapPane,new p(0,0));var n=!this._loaded,o=(this._loaded=!0,e=this._limitZoom(e),this.fire("viewprereset"),this._zoom!==e);this._moveStart(o,i)._move(t,e)._moveEnd(o),this.fire("viewreset"),n&&this.fire("load")},_moveStart:function(t,e){return t&&this.fire("zoomstart"),e||this.fire("movestart"),this},_move:function(t,e,i,n){void 0===e&&(e=this._zoom);var o=this._zoom!==e;return this._zoom=e,this._lastCenter=t,this._pixelOrigin=this._getNewPixelOrigin(t),n?i&&i.pinch&&this.fire("zoom",i):((o||i&&i.pinch)&&this.fire("zoom",i),this.fire("move",i)),this},_moveEnd:function(t){return t&&this.fire("zoomend"),this.fire("moveend")},_stop:function(){return r(this._flyToFrame),this._panAnim&&this._panAnim.stop(),this},_rawPanBy:function(t){Z(this._mapPane,this._getMapPanePos().subtract(t))},_getZoomSpan:function(){return this.getMaxZoom()-this.getMinZoom()},_panInsideMaxBounds:function(){this._enforcingBounds||this.panInsideBounds(this.options.maxBounds)},_checkIfLoaded:function(){if(!this._loaded)throw new Error("Set map center and zoom first.")},_initEvents:function(t){this._targets={};var e=t?k:S;e((this._targets[h(this._container)]=this)._container,"click dblclick mousedown mouseup mouseover mouseout mousemove contextmenu keypress keydown keyup",this._handleDOMEvent,this),this.options.trackResize&&e(window,"resize",this._onResize,this),b.any3d&&this.options.transform3DLimit&&(t?this.off:this.on).call(this,"moveend",this._onMoveEnd)},_onResize:function(){r(this._resizeRequest),this._resizeRequest=x(function(){this.invalidateSize({debounceMoveend:!0})},this)},_onScroll:function(){this._container.scrollTop=0,this._container.scrollLeft=0},_onMoveEnd:function(){var t=this._getMapPanePos();Math.max(Math.abs(t.x),Math.abs(t.y))>=this.options.transform3DLimit&&this._resetView(this.getCenter(),this.getZoom())},_findEventTargets:function(t,e){for(var i,n=[],o="mouseout"===e||"mouseover"===e,s=t.target||t.srcElement,r=!1;s;){if((i=this._targets[h(s)])&&("click"===e||"preclick"===e)&&this._draggableMoved(i)){r=!0;break}if(i&&i.listens(e,!0)){if(o&&!We(s,t))break;if(n.push(i),o)break}if(s===this._container)break;s=s.parentNode}return n=n.length||r||o||!this.listens(e,!0)?n:[this]},_isClickDisabled:function(t){for(;t&&t!==this._container;){if(t._leaflet_disable_click)return!0;t=t.parentNode}},_handleDOMEvent:function(t){var e,i=t.target||t.srcElement;!this._loaded||i._leaflet_disable_events||"click"===t.type&&this._isClickDisabled(i)||("mousedown"===(e=t.type)&&Me(i),this._fireDOMEvent(t,e))},_mouseEvents:["click","dblclick","mouseover","mouseout","contextmenu"],_fireDOMEvent:function(t,e,i){"click"===t.type&&((a=l({},t)).type="preclick",this._fireDOMEvent(a,a.type,i));var n=this._findEventTargets(t,e);if(i){for(var o=[],s=0;sthis.options.zoomAnimationThreshold)return!1;var n=this.getZoomScale(e),n=this._getCenterOffset(t)._divideBy(1-1/n);if(!0!==i.animate&&!this.getSize().contains(n))return!1;x(function(){this._moveStart(!0,i.noMoveStart||!1)._animateZoom(t,e,!0)},this)}return!0},_animateZoom:function(t,e,i,n){this._mapPane&&(i&&(this._animatingZoom=!0,this._animateToCenter=t,this._animateToZoom=e,M(this._mapPane,"leaflet-zoom-anim")),this.fire("zoomanim",{center:t,zoom:e,noUpdate:n}),this._tempFireZoomEvent||(this._tempFireZoomEvent=this._zoom!==this._animateToZoom),this._move(this._animateToCenter,this._animateToZoom,void 0,!0),setTimeout(a(this._onZoomTransitionEnd,this),250))},_onZoomTransitionEnd:function(){this._animatingZoom&&(this._mapPane&&z(this._mapPane,"leaflet-zoom-anim"),this._animatingZoom=!1,this._move(this._animateToCenter,this._animateToZoom,void 0,!0),this._tempFireZoomEvent&&this.fire("zoom"),delete this._tempFireZoomEvent,this.fire("move"),this._moveEnd(!0))}});function Ue(t){return new B(t)}var B=et.extend({options:{position:"topright"},initialize:function(t){c(this,t)},getPosition:function(){return this.options.position},setPosition:function(t){var e=this._map;return e&&e.removeControl(this),this.options.position=t,e&&e.addControl(this),this},getContainer:function(){return this._container},addTo:function(t){this.remove(),this._map=t;var e=this._container=this.onAdd(t),i=this.getPosition(),t=t._controlCorners[i];return M(e,"leaflet-control"),-1!==i.indexOf("bottom")?t.insertBefore(e,t.firstChild):t.appendChild(e),this._map.on("unload",this.remove,this),this},remove:function(){return this._map&&(T(this._container),this.onRemove&&this.onRemove(this._map),this._map.off("unload",this.remove,this),this._map=null),this},_refocusOnMap:function(t){this._map&&t&&0",e=document.createElement("div");return e.innerHTML=t,e.firstChild},_addItem:function(t){var e,i=document.createElement("label"),n=this._map.hasLayer(t.layer),n=(t.overlay?((e=document.createElement("input")).type="checkbox",e.className="leaflet-control-layers-selector",e.defaultChecked=n):e=this._createRadioElement("leaflet-base-layers_"+h(this),n),this._layerControlInputs.push(e),e.layerId=h(t.layer),S(e,"click",this._onInputClick,this),document.createElement("span")),o=(n.innerHTML=" "+t.name,document.createElement("span"));return i.appendChild(o),o.appendChild(e),o.appendChild(n),(t.overlay?this._overlaysList:this._baseLayersList).appendChild(i),this._checkDisabledLayers(),i},_onInputClick:function(){if(!this._preventClick){var t,e,i=this._layerControlInputs,n=[],o=[];this._handlingClick=!0;for(var s=i.length-1;0<=s;s--)t=i[s],e=this._getLayer(t.layerId).layer,t.checked?n.push(e):t.checked||o.push(e);for(s=0;se.options.maxZoom},_expandIfNotCollapsed:function(){return this._map&&!this.options.collapsed&&this.expand(),this},_expandSafely:function(){var t=this._section,e=(this._preventClick=!0,S(t,"click",O),this.expand(),this);setTimeout(function(){k(t,"click",O),e._preventClick=!1})}})),qe=B.extend({options:{position:"topleft",zoomInText:'',zoomInTitle:"Zoom in",zoomOutText:'',zoomOutTitle:"Zoom out"},onAdd:function(t){var e="leaflet-control-zoom",i=P("div",e+" leaflet-bar"),n=this.options;return this._zoomInButton=this._createButton(n.zoomInText,n.zoomInTitle,e+"-in",i,this._zoomIn),this._zoomOutButton=this._createButton(n.zoomOutText,n.zoomOutTitle,e+"-out",i,this._zoomOut),this._updateDisabled(),t.on("zoomend zoomlevelschange",this._updateDisabled,this),i},onRemove:function(t){t.off("zoomend zoomlevelschange",this._updateDisabled,this)},disable:function(){return this._disabled=!0,this._updateDisabled(),this},enable:function(){return this._disabled=!1,this._updateDisabled(),this},_zoomIn:function(t){!this._disabled&&this._map._zoomthis._map.getMinZoom()&&this._map.zoomOut(this._map.options.zoomDelta*(t.shiftKey?3:1))},_createButton:function(t,e,i,n,o){i=P("a",i,n);return i.innerHTML=t,i.href="#",i.title=e,i.setAttribute("role","button"),i.setAttribute("aria-label",e),Ie(i),S(i,"click",Re),S(i,"click",o,this),S(i,"click",this._refocusOnMap,this),i},_updateDisabled:function(){var t=this._map,e="leaflet-disabled";z(this._zoomInButton,e),z(this._zoomOutButton,e),this._zoomInButton.setAttribute("aria-disabled","false"),this._zoomOutButton.setAttribute("aria-disabled","false"),!this._disabled&&t._zoom!==t.getMinZoom()||(M(this._zoomOutButton,e),this._zoomOutButton.setAttribute("aria-disabled","true")),!this._disabled&&t._zoom!==t.getMaxZoom()||(M(this._zoomInButton,e),this._zoomInButton.setAttribute("aria-disabled","true"))}}),Ge=(A.mergeOptions({zoomControl:!0}),A.addInitHook(function(){this.options.zoomControl&&(this.zoomControl=new qe,this.addControl(this.zoomControl))}),B.extend({options:{position:"bottomleft",maxWidth:100,metric:!0,imperial:!0},onAdd:function(t){var e="leaflet-control-scale",i=P("div",e),n=this.options;return this._addScales(n,e+"-line",i),t.on(n.updateWhenIdle?"moveend":"move",this._update,this),t.whenReady(this._update,this),i},onRemove:function(t){t.off(this.options.updateWhenIdle?"moveend":"move",this._update,this)},_addScales:function(t,e,i){t.metric&&(this._mScale=P("div",e,i)),t.imperial&&(this._iScale=P("div",e,i))},_update:function(){var t=this._map,e=t.getSize().y/2,t=t.distance(t.containerPointToLatLng([0,e]),t.containerPointToLatLng([this.options.maxWidth,e]));this._updateScales(t)},_updateScales:function(t){this.options.metric&&t&&this._updateMetric(t),this.options.imperial&&t&&this._updateImperial(t)},_updateMetric:function(t){var e=this._getRoundNum(t);this._updateScale(this._mScale,e<1e3?e+" m":e/1e3+" km",e/t)},_updateImperial:function(t){var e,i,t=3.2808399*t;5280'+(b.inlineSvg?' ':"")+"Leaflet"},initialize:function(t){c(this,t),this._attributions={}},onAdd:function(t){for(var e in(t.attributionControl=this)._container=P("div","leaflet-control-attribution"),Ie(this._container),t._layers)t._layers[e].getAttribution&&this.addAttribution(t._layers[e].getAttribution());return this._update(),t.on("layeradd",this._addAttribution,this),this._container},onRemove:function(t){t.off("layeradd",this._addAttribution,this)},_addAttribution:function(t){t.layer.getAttribution&&(this.addAttribution(t.layer.getAttribution()),t.layer.once("remove",function(){this.removeAttribution(t.layer.getAttribution())},this))},setPrefix:function(t){return this.options.prefix=t,this._update(),this},addAttribution:function(t){return t&&(this._attributions[t]||(this._attributions[t]=0),this._attributions[t]++,this._update()),this},removeAttribution:function(t){return t&&this._attributions[t]&&(this._attributions[t]--,this._update()),this},_update:function(){if(this._map){var t,e=[];for(t in this._attributions)this._attributions[t]&&e.push(t);var i=[];this.options.prefix&&i.push(this.options.prefix),e.length&&i.push(e.join(", ")),this._container.innerHTML=i.join(' ')}}}),n=(A.mergeOptions({attributionControl:!0}),A.addInitHook(function(){this.options.attributionControl&&(new Ke).addTo(this)}),B.Layers=Ve,B.Zoom=qe,B.Scale=Ge,B.Attribution=Ke,Ue.layers=function(t,e,i){return new Ve(t,e,i)},Ue.zoom=function(t){return new qe(t)},Ue.scale=function(t){return new Ge(t)},Ue.attribution=function(t){return new Ke(t)},et.extend({initialize:function(t){this._map=t},enable:function(){return this._enabled||(this._enabled=!0,this.addHooks()),this},disable:function(){return this._enabled&&(this._enabled=!1,this.removeHooks()),this},enabled:function(){return!!this._enabled}})),ft=(n.addTo=function(t,e){return t.addHandler(e,this),this},{Events:e}),Ye=b.touch?"touchstart mousedown":"mousedown",Xe=it.extend({options:{clickTolerance:3},initialize:function(t,e,i,n){c(this,n),this._element=t,this._dragStartTarget=e||t,this._preventOutline=i},enable:function(){this._enabled||(S(this._dragStartTarget,Ye,this._onDown,this),this._enabled=!0)},disable:function(){this._enabled&&(Xe._dragging===this&&this.finishDrag(!0),k(this._dragStartTarget,Ye,this._onDown,this),this._enabled=!1,this._moved=!1)},_onDown:function(t){var e,i;this._enabled&&(this._moved=!1,ve(this._element,"leaflet-zoom-anim")||(t.touches&&1!==t.touches.length?Xe._dragging===this&&this.finishDrag():Xe._dragging||t.shiftKey||1!==t.which&&1!==t.button&&!t.touches||((Xe._dragging=this)._preventOutline&&Me(this._element),Le(),re(),this._moving||(this.fire("down"),i=t.touches?t.touches[0]:t,e=Ce(this._element),this._startPoint=new p(i.clientX,i.clientY),this._startPos=Pe(this._element),this._parentScale=Ze(e),i="mousedown"===t.type,S(document,i?"mousemove":"touchmove",this._onMove,this),S(document,i?"mouseup":"touchend touchcancel",this._onUp,this)))))},_onMove:function(t){var e;this._enabled&&(t.touches&&1e&&(i.push(t[n]),o=n);oe.max.x&&(i|=2),t.ye.max.y&&(i|=8),i}function ri(t,e,i,n){var o=e.x,e=e.y,s=i.x-o,r=i.y-e,a=s*s+r*r;return 0this._layersMaxZoom&&this.setZoom(this._layersMaxZoom),void 0===this.options.minZoom&&this._layersMinZoom&&this.getZoom()t.y!=n.y>t.y&&t.x<(n.x-i.x)*(t.y-i.y)/(n.y-i.y)+i.x&&(l=!l);return l||yi.prototype._containsPoint.call(this,t,!0)}});var wi=ci.extend({initialize:function(t,e){c(this,e),this._layers={},t&&this.addData(t)},addData:function(t){var e,i,n,o=d(t)?t:t.features;if(o){for(e=0,i=o.length;es.x&&(r=i.x+a-s.x+o.x),i.x-r-n.x<(a=0)&&(r=i.x-n.x),i.y+e+o.y>s.y&&(a=i.y+e-s.y+o.y),i.y-a-n.y<0&&(a=i.y-n.y),(r||a)&&(this.options.keepInView&&(this._autopanning=!0),t.fire("autopanstart").panBy([r,a]))))},_getAnchor:function(){return m(this._source&&this._source._getPopupAnchor?this._source._getPopupAnchor():[0,0])}})),Ii=(A.mergeOptions({closePopupOnClick:!0}),A.include({openPopup:function(t,e,i){return this._initOverlay(Bi,t,e,i).openOn(this),this},closePopup:function(t){return(t=arguments.length?t:this._popup)&&t.close(),this}}),o.include({bindPopup:function(t,e){return this._popup=this._initOverlay(Bi,this._popup,t,e),this._popupHandlersAdded||(this.on({click:this._openPopup,keypress:this._onKeyPress,remove:this.closePopup,move:this._movePopup}),this._popupHandlersAdded=!0),this},unbindPopup:function(){return this._popup&&(this.off({click:this._openPopup,keypress:this._onKeyPress,remove:this.closePopup,move:this._movePopup}),this._popupHandlersAdded=!1,this._popup=null),this},openPopup:function(t){return this._popup&&(this instanceof ci||(this._popup._source=this),this._popup._prepareOpen(t||this._latlng)&&this._popup.openOn(this._map)),this},closePopup:function(){return this._popup&&this._popup.close(),this},togglePopup:function(){return this._popup&&this._popup.toggle(this),this},isPopupOpen:function(){return!!this._popup&&this._popup.isOpen()},setPopupContent:function(t){return this._popup&&this._popup.setContent(t),this},getPopup:function(){return this._popup},_openPopup:function(t){var e;this._popup&&this._map&&(Re(t),e=t.layer||t.target,this._popup._source!==e||e instanceof fi?(this._popup._source=e,this.openPopup(t.latlng)):this._map.hasLayer(this._popup)?this.closePopup():this.openPopup(t.latlng))},_movePopup:function(t){this._popup.setLatLng(t.latlng)},_onKeyPress:function(t){13===t.originalEvent.keyCode&&this._openPopup(t)}}),Ai.extend({options:{pane:"tooltipPane",offset:[0,0],direction:"auto",permanent:!1,sticky:!1,opacity:.9},onAdd:function(t){Ai.prototype.onAdd.call(this,t),this.setOpacity(this.options.opacity),t.fire("tooltipopen",{tooltip:this}),this._source&&(this.addEventParent(this._source),this._source.fire("tooltipopen",{tooltip:this},!0))},onRemove:function(t){Ai.prototype.onRemove.call(this,t),t.fire("tooltipclose",{tooltip:this}),this._source&&(this.removeEventParent(this._source),this._source.fire("tooltipclose",{tooltip:this},!0))},getEvents:function(){var t=Ai.prototype.getEvents.call(this);return this.options.permanent||(t.preclick=this.close),t},_initLayout:function(){var t="leaflet-tooltip "+(this.options.className||"")+" leaflet-zoom-"+(this._zoomAnimated?"animated":"hide");this._contentNode=this._container=P("div",t),this._container.setAttribute("role","tooltip"),this._container.setAttribute("id","leaflet-tooltip-"+h(this))},_updateLayout:function(){},_adjustPan:function(){},_setPosition:function(t){var e,i=this._map,n=this._container,o=i.latLngToContainerPoint(i.getCenter()),i=i.layerPointToContainerPoint(t),s=this.options.direction,r=n.offsetWidth,a=n.offsetHeight,h=m(this.options.offset),l=this._getAnchor(),i="top"===s?(e=r/2,a):"bottom"===s?(e=r/2,0):(e="center"===s?r/2:"right"===s?0:"left"===s?r:i.xthis.options.maxZoom||nthis.options.maxZoom||void 0!==this.options.minZoom&&oi.max.x)||!e.wrapLat&&(t.yi.max.y))return!1}return!this.options.bounds||(e=this._tileCoordsToBounds(t),g(this.options.bounds).overlaps(e))},_keyToBounds:function(t){return this._tileCoordsToBounds(this._keyToTileCoords(t))},_tileCoordsToNwSe:function(t){var e=this._map,i=this.getTileSize(),n=t.scaleBy(i),i=n.add(i);return[e.unproject(n,t.z),e.unproject(i,t.z)]},_tileCoordsToBounds:function(t){t=this._tileCoordsToNwSe(t),t=new s(t[0],t[1]);return t=this.options.noWrap?t:this._map.wrapLatLngBounds(t)},_tileCoordsToKey:function(t){return t.x+":"+t.y+":"+t.z},_keyToTileCoords:function(t){var t=t.split(":"),e=new p(+t[0],+t[1]);return e.z=+t[2],e},_removeTile:function(t){var e=this._tiles[t];e&&(T(e.el),delete this._tiles[t],this.fire("tileunload",{tile:e.el,coords:this._keyToTileCoords(t)}))},_initTile:function(t){M(t,"leaflet-tile");var e=this.getTileSize();t.style.width=e.x+"px",t.style.height=e.y+"px",t.onselectstart=u,t.onmousemove=u,b.ielt9&&this.options.opacity<1&&C(t,this.options.opacity)},_addTile:function(t,e){var i=this._getTilePos(t),n=this._tileCoordsToKey(t),o=this.createTile(this._wrapCoords(t),a(this._tileReady,this,t));this._initTile(o),this.createTile.length<2&&x(a(this._tileReady,this,t,null,o)),Z(o,i),this._tiles[n]={el:o,coords:t,current:!0},e.appendChild(o),this.fire("tileloadstart",{tile:o,coords:t})},_tileReady:function(t,e,i){e&&this.fire("tileerror",{error:e,tile:i,coords:t});var n=this._tileCoordsToKey(t);(i=this._tiles[n])&&(i.loaded=+new Date,this._map._fadeAnimated?(C(i.el,0),r(this._fadeFrame),this._fadeFrame=x(this._updateOpacity,this)):(i.active=!0,this._pruneTiles()),e||(M(i.el,"leaflet-tile-loaded"),this.fire("tileload",{tile:i.el,coords:t})),this._noTilesToLoad()&&(this._loading=!1,this.fire("load"),b.ielt9||!this._map._fadeAnimated?x(this._pruneTiles,this):setTimeout(a(this._pruneTiles,this),250)))},_getTilePos:function(t){return t.scaleBy(this.getTileSize()).subtract(this._level.origin)},_wrapCoords:function(t){var e=new p(this._wrapX?H(t.x,this._wrapX):t.x,this._wrapY?H(t.y,this._wrapY):t.y);return e.z=t.z,e},_pxBoundsToTileRange:function(t){var e=this.getTileSize();return new f(t.min.unscaleBy(e).floor(),t.max.unscaleBy(e).ceil().subtract([1,1]))},_noTilesToLoad:function(){for(var t in this._tiles)if(!this._tiles[t].loaded)return!1;return!0}});var Di=Ni.extend({options:{minZoom:0,maxZoom:18,subdomains:"abc",errorTileUrl:"",zoomOffset:0,tms:!1,zoomReverse:!1,detectRetina:!1,crossOrigin:!1,referrerPolicy:!1},initialize:function(t,e){this._url=t,(e=c(this,e)).detectRetina&&b.retina&&0')}}catch(t){}return function(t){return document.createElement("<"+t+' xmlns="urn:schemas-microsoft.com:vml" class="lvml">')}}(),zt={_initContainer:function(){this._container=P("div","leaflet-vml-container")},_update:function(){this._map._animatingZoom||(Wi.prototype._update.call(this),this.fire("update"))},_initPath:function(t){var e=t._container=Vi("shape");M(e,"leaflet-vml-shape "+(this.options.className||"")),e.coordsize="1 1",t._path=Vi("path"),e.appendChild(t._path),this._updateStyle(t),this._layers[h(t)]=t},_addPath:function(t){var e=t._container;this._container.appendChild(e),t.options.interactive&&t.addInteractiveTarget(e)},_removePath:function(t){var e=t._container;T(e),t.removeInteractiveTarget(e),delete this._layers[h(t)]},_updateStyle:function(t){var e=t._stroke,i=t._fill,n=t.options,o=t._container;o.stroked=!!n.stroke,o.filled=!!n.fill,n.stroke?(e=e||(t._stroke=Vi("stroke")),o.appendChild(e),e.weight=n.weight+"px",e.color=n.color,e.opacity=n.opacity,n.dashArray?e.dashStyle=d(n.dashArray)?n.dashArray.join(" "):n.dashArray.replace(/( *, *)/g," "):e.dashStyle="",e.endcap=n.lineCap.replace("butt","flat"),e.joinstyle=n.lineJoin):e&&(o.removeChild(e),t._stroke=null),n.fill?(i=i||(t._fill=Vi("fill")),o.appendChild(i),i.color=n.fillColor||n.color,i.opacity=n.fillOpacity):i&&(o.removeChild(i),t._fill=null)},_updateCircle:function(t){var e=t._point.round(),i=Math.round(t._radius),n=Math.round(t._radiusY||i);this._setPath(t,t._empty()?"M0 0":"AL "+e.x+","+e.y+" "+i+","+n+" 0,23592600")},_setPath:function(t,e){t._path.v=e},_bringToFront:function(t){fe(t._container)},_bringToBack:function(t){ge(t._container)}},qi=b.vml?Vi:ct,Gi=Wi.extend({_initContainer:function(){this._container=qi("svg"),this._container.setAttribute("pointer-events","none"),this._rootGroup=qi("g"),this._container.appendChild(this._rootGroup)},_destroyContainer:function(){T(this._container),k(this._container),delete this._container,delete this._rootGroup,delete this._svgSize},_update:function(){var t,e,i;this._map._animatingZoom&&this._bounds||(Wi.prototype._update.call(this),e=(t=this._bounds).getSize(),i=this._container,this._svgSize&&this._svgSize.equals(e)||(this._svgSize=e,i.setAttribute("width",e.x),i.setAttribute("height",e.y)),Z(i,t.min),i.setAttribute("viewBox",[t.min.x,t.min.y,e.x,e.y].join(" ")),this.fire("update"))},_initPath:function(t){var e=t._path=qi("path");t.options.className&&M(e,t.options.className),t.options.interactive&&M(e,"leaflet-interactive"),this._updateStyle(t),this._layers[h(t)]=t},_addPath:function(t){this._rootGroup||this._initContainer(),this._rootGroup.appendChild(t._path),t.addInteractiveTarget(t._path)},_removePath:function(t){T(t._path),t.removeInteractiveTarget(t._path),delete this._layers[h(t)]},_updatePath:function(t){t._project(),t._update()},_updateStyle:function(t){var e=t._path,t=t.options;e&&(t.stroke?(e.setAttribute("stroke",t.color),e.setAttribute("stroke-opacity",t.opacity),e.setAttribute("stroke-width",t.weight),e.setAttribute("stroke-linecap",t.lineCap),e.setAttribute("stroke-linejoin",t.lineJoin),t.dashArray?e.setAttribute("stroke-dasharray",t.dashArray):e.removeAttribute("stroke-dasharray"),t.dashOffset?e.setAttribute("stroke-dashoffset",t.dashOffset):e.removeAttribute("stroke-dashoffset")):e.setAttribute("stroke","none"),t.fill?(e.setAttribute("fill",t.fillColor||t.color),e.setAttribute("fill-opacity",t.fillOpacity),e.setAttribute("fill-rule",t.fillRule||"evenodd")):e.setAttribute("fill","none"))},_updatePoly:function(t,e){this._setPath(t,dt(t._parts,e))},_updateCircle:function(t){var e=t._point,i=Math.max(Math.round(t._radius),1),n="a"+i+","+(Math.max(Math.round(t._radiusY),1)||i)+" 0 1,0 ",e=t._empty()?"M0 0":"M"+(e.x-i)+","+e.y+n+2*i+",0 "+n+2*-i+",0 ";this._setPath(t,e)},_setPath:function(t,e){t._path.setAttribute("d",e)},_bringToFront:function(t){fe(t._path)},_bringToBack:function(t){ge(t._path)}});function Ki(t){return b.svg||b.vml?new Gi(t):null}b.vml&&Gi.include(zt),A.include({getRenderer:function(t){t=(t=t.options.renderer||this._getPaneRenderer(t.options.pane)||this.options.renderer||this._renderer)||(this._renderer=this._createRenderer());return this.hasLayer(t)||this.addLayer(t),t},_getPaneRenderer:function(t){var e;return"overlayPane"!==t&&void 0!==t&&(void 0===(e=this._paneRenderers[t])&&(e=this._createRenderer({pane:t}),this._paneRenderers[t]=e),e)},_createRenderer:function(t){return this.options.preferCanvas&&Ui(t)||Ki(t)}});var Yi=xi.extend({initialize:function(t,e){xi.prototype.initialize.call(this,this._boundsToLatLngs(t),e)},setBounds:function(t){return this.setLatLngs(this._boundsToLatLngs(t))},_boundsToLatLngs:function(t){return[(t=g(t)).getSouthWest(),t.getNorthWest(),t.getNorthEast(),t.getSouthEast()]}});Gi.create=qi,Gi.pointsToPath=dt,wi.geometryToLayer=bi,wi.coordsToLatLng=Li,wi.coordsToLatLngs=Ti,wi.latLngToCoords=Mi,wi.latLngsToCoords=zi,wi.getFeature=Ci,wi.asFeature=Zi,A.mergeOptions({boxZoom:!0});var _t=n.extend({initialize:function(t){this._map=t,this._container=t._container,this._pane=t._panes.overlayPane,this._resetStateTimeout=0,t.on("unload",this._destroy,this)},addHooks:function(){S(this._container,"mousedown",this._onMouseDown,this)},removeHooks:function(){k(this._container,"mousedown",this._onMouseDown,this)},moved:function(){return this._moved},_destroy:function(){T(this._pane),delete this._pane},_resetState:function(){this._resetStateTimeout=0,this._moved=!1},_clearDeferredResetState:function(){0!==this._resetStateTimeout&&(clearTimeout(this._resetStateTimeout),this._resetStateTimeout=0)},_onMouseDown:function(t){if(!t.shiftKey||1!==t.which&&1!==t.button)return!1;this._clearDeferredResetState(),this._resetState(),re(),Le(),this._startPoint=this._map.mouseEventToContainerPoint(t),S(document,{contextmenu:Re,mousemove:this._onMouseMove,mouseup:this._onMouseUp,keydown:this._onKeyDown},this)},_onMouseMove:function(t){this._moved||(this._moved=!0,this._box=P("div","leaflet-zoom-box",this._container),M(this._container,"leaflet-crosshair"),this._map.fire("boxzoomstart")),this._point=this._map.mouseEventToContainerPoint(t);var t=new f(this._point,this._startPoint),e=t.getSize();Z(this._box,t.min),this._box.style.width=e.x+"px",this._box.style.height=e.y+"px"},_finish:function(){this._moved&&(T(this._box),z(this._container,"leaflet-crosshair")),ae(),Te(),k(document,{contextmenu:Re,mousemove:this._onMouseMove,mouseup:this._onMouseUp,keydown:this._onKeyDown},this)},_onMouseUp:function(t){1!==t.which&&1!==t.button||(this._finish(),this._moved&&(this._clearDeferredResetState(),this._resetStateTimeout=setTimeout(a(this._resetState,this),0),t=new s(this._map.containerPointToLatLng(this._startPoint),this._map.containerPointToLatLng(this._point)),this._map.fitBounds(t).fire("boxzoomend",{boxZoomBounds:t})))},_onKeyDown:function(t){27===t.keyCode&&(this._finish(),this._clearDeferredResetState(),this._resetState())}}),Ct=(A.addInitHook("addHandler","boxZoom",_t),A.mergeOptions({doubleClickZoom:!0}),n.extend({addHooks:function(){this._map.on("dblclick",this._onDoubleClick,this)},removeHooks:function(){this._map.off("dblclick",this._onDoubleClick,this)},_onDoubleClick:function(t){var e=this._map,i=e.getZoom(),n=e.options.zoomDelta,i=t.originalEvent.shiftKey?i-n:i+n;"center"===e.options.doubleClickZoom?e.setZoom(i):e.setZoomAround(t.containerPoint,i)}})),Zt=(A.addInitHook("addHandler","doubleClickZoom",Ct),A.mergeOptions({dragging:!0,inertia:!0,inertiaDeceleration:3400,inertiaMaxSpeed:1/0,easeLinearity:.2,worldCopyJump:!1,maxBoundsViscosity:0}),n.extend({addHooks:function(){var t;this._draggable||(t=this._map,this._draggable=new Xe(t._mapPane,t._container),this._draggable.on({dragstart:this._onDragStart,drag:this._onDrag,dragend:this._onDragEnd},this),this._draggable.on("predrag",this._onPreDragLimit,this),t.options.worldCopyJump&&(this._draggable.on("predrag",this._onPreDragWrap,this),t.on("zoomend",this._onZoomEnd,this),t.whenReady(this._onZoomEnd,this))),M(this._map._container,"leaflet-grab leaflet-touch-drag"),this._draggable.enable(),this._positions=[],this._times=[]},removeHooks:function(){z(this._map._container,"leaflet-grab"),z(this._map._container,"leaflet-touch-drag"),this._draggable.disable()},moved:function(){return this._draggable&&this._draggable._moved},moving:function(){return this._draggable&&this._draggable._moving},_onDragStart:function(){var t,e=this._map;e._stop(),this._map.options.maxBounds&&this._map.options.maxBoundsViscosity?(t=g(this._map.options.maxBounds),this._offsetLimit=_(this._map.latLngToContainerPoint(t.getNorthWest()).multiplyBy(-1),this._map.latLngToContainerPoint(t.getSouthEast()).multiplyBy(-1).add(this._map.getSize())),this._viscosity=Math.min(1,Math.max(0,this._map.options.maxBoundsViscosity))):this._offsetLimit=null,e.fire("movestart").fire("dragstart"),e.options.inertia&&(this._positions=[],this._times=[])},_onDrag:function(t){var e,i;this._map.options.inertia&&(e=this._lastTime=+new Date,i=this._lastPos=this._draggable._absPos||this._draggable._newPos,this._positions.push(i),this._times.push(e),this._prunePositions(e)),this._map.fire("move",t).fire("drag",t)},_prunePositions:function(t){for(;1e.max.x&&(t.x=this._viscousLimit(t.x,e.max.x)),t.y>e.max.y&&(t.y=this._viscousLimit(t.y,e.max.y)),this._draggable._newPos=this._draggable._startPos.add(t))},_onPreDragWrap:function(){var t=this._worldWidth,e=Math.round(t/2),i=this._initialWorldOffset,n=this._draggable._newPos.x,o=(n-e+i)%t+e-i,n=(n+e+i)%t-e-i,t=Math.abs(o+i)e.getMaxZoom()&&1 { + self.skipWaiting(); +}); + +self.addEventListener('activate', (event) => { + event.waitUntil((async () => { + const keys = await caches.keys(); + await Promise.all( + keys + .filter((k) => ![TILE_CACHE, ASSET_CACHE, APP_CACHE].includes(k)) + .map((k) => caches.delete(k)) + ); + await self.clients.claim(); + })()); +}); + +function isTileRequest(url) { + return url.pathname.startsWith('/tiles/') || url.pathname.startsWith('/vtiles/'); +} + +function isLongLivedAsset(url) { + if (url.pathname.startsWith('/static/leaflet/')) return true; + if (url.pathname.startsWith('/static/xterm/')) return true; + if (url.pathname.startsWith('/svg/')) return true; + return false; +} + +function isAppShell(url) { + if (url.pathname === '/' || url.pathname === '/cert') return true; + if (url.pathname.startsWith('/static/js/')) return true; + if (url.pathname.startsWith('/static/css/')) return true; + return false; +} + +function isApiRequest(url) { + return url.pathname.startsWith('/api/') || url.pathname === '/ws' || url.pathname.startsWith('/ws/'); +} + +async function trimCache(cacheName, max) { + try { + const cache = await caches.open(cacheName); + const keys = await cache.keys(); + if (keys.length <= max) return; + const excess = keys.length - max; + for (let i = 0; i < excess; i += 1) { + await cache.delete(keys[i]); + } + } catch (e) {} +} + +// Cache-first с фоновым обновлением: отдаём из кеша если есть, в фоне освежаем. +async function cacheFirstWithRefresh(request, cacheName) { + const cache = await caches.open(cacheName); + const cached = await cache.match(request); + const fetchAndUpdate = fetch(request) + .then((resp) => { + if (resp && resp.ok) { + cache.put(request, resp.clone()).catch(() => {}); + if (cacheName === TILE_CACHE) { + trimCache(TILE_CACHE, TILE_CACHE_MAX); + } + } + return resp; + }) + .catch(() => null); + if (cached) { + // Обновление в фоне, клиенту отдаём кеш немедленно. + fetchAndUpdate.catch(() => {}); + return cached; + } + const fresh = await fetchAndUpdate; + if (fresh) return fresh; + return new Response('', { status: 504, statusText: 'Gateway Timeout (offline)' }); +} + +// Stale-while-revalidate: мгновенно из кеша, параллельно обновляем кеш. +async function staleWhileRevalidate(request, cacheName) { + const cache = await caches.open(cacheName); + const cached = await cache.match(request); + const networkFetch = fetch(request) + .then((resp) => { + if (resp && resp.ok) { + cache.put(request, resp.clone()).catch(() => {}); + } + return resp; + }) + .catch(() => null); + if (cached) { + networkFetch.catch(() => {}); + return cached; + } + const fresh = await networkFetch; + if (fresh) return fresh; + return new Response('', { status: 504, statusText: 'Gateway Timeout (offline)' }); +} + +self.addEventListener('fetch', (event) => { + const req = event.request; + if (req.method !== 'GET') return; + + let url; + try { + url = new URL(req.url); + } catch (e) { + return; + } + + // Не трогаем кросс-оригинальные запросы (OSM, CARTO и т.п.) — пусть идут напрямую. + if (url.origin !== self.location.origin) return; + + // API и WebSocket — без кеша. WebSocket вообще не проходит через fetch, но на всякий случай. + if (isApiRequest(url)) return; + + if (isTileRequest(url)) { + event.respondWith(cacheFirstWithRefresh(req, TILE_CACHE)); + return; + } + + if (isLongLivedAsset(url)) { + event.respondWith(cacheFirstWithRefresh(req, ASSET_CACHE)); + return; + } + + if (isAppShell(url)) { + event.respondWith(staleWhileRevalidate(req, APP_CACHE)); + return; + } +}); + +// Принудительный сброс кешей по команде со страницы. +self.addEventListener('message', (event) => { + if (!event.data || typeof event.data !== 'object') return; + if (event.data.type === 'aismap:clear-caches') { + event.waitUntil((async () => { + const keys = await caches.keys(); + await Promise.all(keys.map((k) => caches.delete(k))); + })()); + } +}); diff --git a/static/tiles/1/1/0.png b/static/tiles/1/1/0.png new file mode 100644 index 0000000..622b3ee Binary files /dev/null and b/static/tiles/1/1/0.png differ diff --git a/static/tiles/10/618/318.png b/static/tiles/10/618/318.png new file mode 100644 index 0000000..1cf24e3 Binary files /dev/null and b/static/tiles/10/618/318.png differ diff --git a/static/tiles/10/618/319.png b/static/tiles/10/618/319.png new file mode 100644 index 0000000..16260f5 Binary files /dev/null and b/static/tiles/10/618/319.png differ diff --git a/static/tiles/10/618/320.png b/static/tiles/10/618/320.png new file mode 100644 index 0000000..95c0de5 Binary files /dev/null and b/static/tiles/10/618/320.png differ diff --git a/static/tiles/10/618/321.png b/static/tiles/10/618/321.png new file mode 100644 index 0000000..443bbe4 Binary files /dev/null and b/static/tiles/10/618/321.png differ diff --git a/static/tiles/10/619/318.png b/static/tiles/10/619/318.png new file mode 100644 index 0000000..11bf73a Binary files /dev/null and b/static/tiles/10/619/318.png differ diff --git a/static/tiles/10/619/319.png b/static/tiles/10/619/319.png new file mode 100644 index 0000000..b3f684d Binary files /dev/null and b/static/tiles/10/619/319.png differ diff --git a/static/tiles/10/619/320.png b/static/tiles/10/619/320.png new file mode 100644 index 0000000..faea769 Binary files /dev/null and b/static/tiles/10/619/320.png differ diff --git a/static/tiles/10/619/321.png b/static/tiles/10/619/321.png new file mode 100644 index 0000000..e6ec375 Binary files /dev/null and b/static/tiles/10/619/321.png differ diff --git a/static/tiles/11/1236/637.png b/static/tiles/11/1236/637.png new file mode 100644 index 0000000..f782e38 Binary files /dev/null and b/static/tiles/11/1236/637.png differ diff --git a/static/tiles/11/1236/638.png b/static/tiles/11/1236/638.png new file mode 100644 index 0000000..7899d44 Binary files /dev/null and b/static/tiles/11/1236/638.png differ diff --git a/static/tiles/11/1236/639.png b/static/tiles/11/1236/639.png new file mode 100644 index 0000000..154356a Binary files /dev/null and b/static/tiles/11/1236/639.png differ diff --git a/static/tiles/11/1236/640.png b/static/tiles/11/1236/640.png new file mode 100644 index 0000000..ca706c8 Binary files /dev/null and b/static/tiles/11/1236/640.png differ diff --git a/static/tiles/11/1236/641.png b/static/tiles/11/1236/641.png new file mode 100644 index 0000000..b4809d7 Binary files /dev/null and b/static/tiles/11/1236/641.png differ diff --git a/static/tiles/11/1236/642.png b/static/tiles/11/1236/642.png new file mode 100644 index 0000000..47efe06 Binary files /dev/null and b/static/tiles/11/1236/642.png differ diff --git a/static/tiles/11/1237/637.png b/static/tiles/11/1237/637.png new file mode 100644 index 0000000..608e18d Binary files /dev/null and b/static/tiles/11/1237/637.png differ diff --git a/static/tiles/11/1237/638.png b/static/tiles/11/1237/638.png new file mode 100644 index 0000000..cc24cfa Binary files /dev/null and b/static/tiles/11/1237/638.png differ diff --git a/static/tiles/11/1237/639.png b/static/tiles/11/1237/639.png new file mode 100644 index 0000000..eb50888 Binary files /dev/null and b/static/tiles/11/1237/639.png differ diff --git a/static/tiles/11/1237/640.png b/static/tiles/11/1237/640.png new file mode 100644 index 0000000..50716f3 Binary files /dev/null and b/static/tiles/11/1237/640.png differ diff --git a/static/tiles/11/1237/641.png b/static/tiles/11/1237/641.png new file mode 100644 index 0000000..938762c Binary files /dev/null and b/static/tiles/11/1237/641.png differ diff --git a/static/tiles/11/1237/642.png b/static/tiles/11/1237/642.png new file mode 100644 index 0000000..2f972b0 Binary files /dev/null and b/static/tiles/11/1237/642.png differ diff --git a/static/tiles/11/1238/637.png b/static/tiles/11/1238/637.png new file mode 100644 index 0000000..4516b63 Binary files /dev/null and b/static/tiles/11/1238/637.png differ diff --git a/static/tiles/11/1238/638.png b/static/tiles/11/1238/638.png new file mode 100644 index 0000000..47232bd Binary files /dev/null and b/static/tiles/11/1238/638.png differ diff --git a/static/tiles/11/1238/639.png b/static/tiles/11/1238/639.png new file mode 100644 index 0000000..f97d4cb Binary files /dev/null and b/static/tiles/11/1238/639.png differ diff --git a/static/tiles/11/1238/640.png b/static/tiles/11/1238/640.png new file mode 100644 index 0000000..7dd6151 Binary files /dev/null and b/static/tiles/11/1238/640.png differ diff --git a/static/tiles/11/1238/641.png b/static/tiles/11/1238/641.png new file mode 100644 index 0000000..3eb9eb9 Binary files /dev/null and b/static/tiles/11/1238/641.png differ diff --git a/static/tiles/11/1238/642.png b/static/tiles/11/1238/642.png new file mode 100644 index 0000000..00b5c8b Binary files /dev/null and b/static/tiles/11/1238/642.png differ diff --git a/static/tiles/11/1239/637.png b/static/tiles/11/1239/637.png new file mode 100644 index 0000000..6d5870b Binary files /dev/null and b/static/tiles/11/1239/637.png differ diff --git a/static/tiles/11/1239/638.png b/static/tiles/11/1239/638.png new file mode 100644 index 0000000..4b17032 Binary files /dev/null and b/static/tiles/11/1239/638.png differ diff --git a/static/tiles/11/1239/639.png b/static/tiles/11/1239/639.png new file mode 100644 index 0000000..729a745 Binary files /dev/null and b/static/tiles/11/1239/639.png differ diff --git a/static/tiles/11/1239/640.png b/static/tiles/11/1239/640.png new file mode 100644 index 0000000..a8edb4b Binary files /dev/null and b/static/tiles/11/1239/640.png differ diff --git a/static/tiles/11/1239/641.png b/static/tiles/11/1239/641.png new file mode 100644 index 0000000..1b1af0a Binary files /dev/null and b/static/tiles/11/1239/641.png differ diff --git a/static/tiles/11/1239/642.png b/static/tiles/11/1239/642.png new file mode 100644 index 0000000..46b7691 Binary files /dev/null and b/static/tiles/11/1239/642.png differ diff --git a/static/tiles/12/2472/1275.png b/static/tiles/12/2472/1275.png new file mode 100644 index 0000000..3e3cbf3 Binary files /dev/null and b/static/tiles/12/2472/1275.png differ diff --git a/static/tiles/12/2472/1276.png b/static/tiles/12/2472/1276.png new file mode 100644 index 0000000..fa2589a Binary files /dev/null and b/static/tiles/12/2472/1276.png differ diff --git a/static/tiles/12/2472/1277.png b/static/tiles/12/2472/1277.png new file mode 100644 index 0000000..55ebbbf Binary files /dev/null and b/static/tiles/12/2472/1277.png differ diff --git a/static/tiles/12/2472/1278.png b/static/tiles/12/2472/1278.png new file mode 100644 index 0000000..a3bea51 Binary files /dev/null and b/static/tiles/12/2472/1278.png differ diff --git a/static/tiles/12/2472/1279.png b/static/tiles/12/2472/1279.png new file mode 100644 index 0000000..ef99158 Binary files /dev/null and b/static/tiles/12/2472/1279.png differ diff --git a/static/tiles/12/2472/1280.png b/static/tiles/12/2472/1280.png new file mode 100644 index 0000000..ece7a6f Binary files /dev/null and b/static/tiles/12/2472/1280.png differ diff --git a/static/tiles/12/2472/1281.png b/static/tiles/12/2472/1281.png new file mode 100644 index 0000000..c3b2274 Binary files /dev/null and b/static/tiles/12/2472/1281.png differ diff --git a/static/tiles/12/2472/1282.png b/static/tiles/12/2472/1282.png new file mode 100644 index 0000000..a05bef5 Binary files /dev/null and b/static/tiles/12/2472/1282.png differ diff --git a/static/tiles/12/2472/1283.png b/static/tiles/12/2472/1283.png new file mode 100644 index 0000000..a95507d Binary files /dev/null and b/static/tiles/12/2472/1283.png differ diff --git a/static/tiles/12/2472/1284.png b/static/tiles/12/2472/1284.png new file mode 100644 index 0000000..5fb3883 Binary files /dev/null and b/static/tiles/12/2472/1284.png differ diff --git a/static/tiles/12/2472/1285.png b/static/tiles/12/2472/1285.png new file mode 100644 index 0000000..19ba0df Binary files /dev/null and b/static/tiles/12/2472/1285.png differ diff --git a/static/tiles/12/2473/1275.png b/static/tiles/12/2473/1275.png new file mode 100644 index 0000000..694ae4b Binary files /dev/null and b/static/tiles/12/2473/1275.png differ diff --git a/static/tiles/12/2473/1276.png b/static/tiles/12/2473/1276.png new file mode 100644 index 0000000..6b303d1 Binary files /dev/null and b/static/tiles/12/2473/1276.png differ diff --git a/static/tiles/12/2473/1277.png b/static/tiles/12/2473/1277.png new file mode 100644 index 0000000..d299eef Binary files /dev/null and b/static/tiles/12/2473/1277.png differ diff --git a/static/tiles/12/2473/1278.png b/static/tiles/12/2473/1278.png new file mode 100644 index 0000000..d483e04 Binary files /dev/null and b/static/tiles/12/2473/1278.png differ diff --git a/static/tiles/12/2473/1279.png b/static/tiles/12/2473/1279.png new file mode 100644 index 0000000..a4ba943 Binary files /dev/null and b/static/tiles/12/2473/1279.png differ diff --git a/static/tiles/12/2473/1280.png b/static/tiles/12/2473/1280.png new file mode 100644 index 0000000..8664a2e Binary files /dev/null and b/static/tiles/12/2473/1280.png differ diff --git a/static/tiles/12/2473/1281.png b/static/tiles/12/2473/1281.png new file mode 100644 index 0000000..6265199 Binary files /dev/null and b/static/tiles/12/2473/1281.png differ diff --git a/static/tiles/12/2473/1282.png b/static/tiles/12/2473/1282.png new file mode 100644 index 0000000..3596ece Binary files /dev/null and b/static/tiles/12/2473/1282.png differ diff --git a/static/tiles/12/2473/1283.png b/static/tiles/12/2473/1283.png new file mode 100644 index 0000000..b46c214 Binary files /dev/null and b/static/tiles/12/2473/1283.png differ diff --git a/static/tiles/12/2473/1284.png b/static/tiles/12/2473/1284.png new file mode 100644 index 0000000..1fa2d8e Binary files /dev/null and b/static/tiles/12/2473/1284.png differ diff --git a/static/tiles/12/2473/1285.png b/static/tiles/12/2473/1285.png new file mode 100644 index 0000000..288b944 Binary files /dev/null and b/static/tiles/12/2473/1285.png differ diff --git a/static/tiles/12/2474/1275.png b/static/tiles/12/2474/1275.png new file mode 100644 index 0000000..9ef8d65 Binary files /dev/null and b/static/tiles/12/2474/1275.png differ diff --git a/static/tiles/12/2474/1276.png b/static/tiles/12/2474/1276.png new file mode 100644 index 0000000..16919ad Binary files /dev/null and b/static/tiles/12/2474/1276.png differ diff --git a/static/tiles/12/2474/1277.png b/static/tiles/12/2474/1277.png new file mode 100644 index 0000000..8f61551 Binary files /dev/null and b/static/tiles/12/2474/1277.png differ diff --git a/static/tiles/12/2474/1278.png b/static/tiles/12/2474/1278.png new file mode 100644 index 0000000..ac0e9aa Binary files /dev/null and b/static/tiles/12/2474/1278.png differ diff --git a/static/tiles/12/2474/1279.png b/static/tiles/12/2474/1279.png new file mode 100644 index 0000000..cddd841 Binary files /dev/null and b/static/tiles/12/2474/1279.png differ diff --git a/static/tiles/12/2474/1280.png b/static/tiles/12/2474/1280.png new file mode 100644 index 0000000..39b0a20 Binary files /dev/null and b/static/tiles/12/2474/1280.png differ diff --git a/static/tiles/12/2474/1281.png b/static/tiles/12/2474/1281.png new file mode 100644 index 0000000..2cc7bf1 Binary files /dev/null and b/static/tiles/12/2474/1281.png differ diff --git a/static/tiles/12/2474/1282.png b/static/tiles/12/2474/1282.png new file mode 100644 index 0000000..b83ec55 Binary files /dev/null and b/static/tiles/12/2474/1282.png differ diff --git a/static/tiles/12/2474/1283.png b/static/tiles/12/2474/1283.png new file mode 100644 index 0000000..88bed2f Binary files /dev/null and b/static/tiles/12/2474/1283.png differ diff --git a/static/tiles/12/2474/1284.png b/static/tiles/12/2474/1284.png new file mode 100644 index 0000000..d834300 Binary files /dev/null and b/static/tiles/12/2474/1284.png differ diff --git a/static/tiles/12/2474/1285.png b/static/tiles/12/2474/1285.png new file mode 100644 index 0000000..7045829 Binary files /dev/null and b/static/tiles/12/2474/1285.png differ diff --git a/static/tiles/12/2475/1275.png b/static/tiles/12/2475/1275.png new file mode 100644 index 0000000..3bed802 Binary files /dev/null and b/static/tiles/12/2475/1275.png differ diff --git a/static/tiles/12/2475/1276.png b/static/tiles/12/2475/1276.png new file mode 100644 index 0000000..b245304 Binary files /dev/null and b/static/tiles/12/2475/1276.png differ diff --git a/static/tiles/12/2475/1277.png b/static/tiles/12/2475/1277.png new file mode 100644 index 0000000..61c1769 Binary files /dev/null and b/static/tiles/12/2475/1277.png differ diff --git a/static/tiles/12/2475/1278.png b/static/tiles/12/2475/1278.png new file mode 100644 index 0000000..e0cf78c Binary files /dev/null and b/static/tiles/12/2475/1278.png differ diff --git a/static/tiles/12/2475/1279.png b/static/tiles/12/2475/1279.png new file mode 100644 index 0000000..d16c64b Binary files /dev/null and b/static/tiles/12/2475/1279.png differ diff --git a/static/tiles/12/2475/1280.png b/static/tiles/12/2475/1280.png new file mode 100644 index 0000000..d354fd3 Binary files /dev/null and b/static/tiles/12/2475/1280.png differ diff --git a/static/tiles/12/2475/1281.png b/static/tiles/12/2475/1281.png new file mode 100644 index 0000000..9b67216 Binary files /dev/null and b/static/tiles/12/2475/1281.png differ diff --git a/static/tiles/12/2475/1282.png b/static/tiles/12/2475/1282.png new file mode 100644 index 0000000..8c5f8b1 Binary files /dev/null and b/static/tiles/12/2475/1282.png differ diff --git a/static/tiles/12/2475/1283.png b/static/tiles/12/2475/1283.png new file mode 100644 index 0000000..6b55372 Binary files /dev/null and b/static/tiles/12/2475/1283.png differ diff --git a/static/tiles/12/2475/1284.png b/static/tiles/12/2475/1284.png new file mode 100644 index 0000000..4615899 Binary files /dev/null and b/static/tiles/12/2475/1284.png differ diff --git a/static/tiles/12/2475/1285.png b/static/tiles/12/2475/1285.png new file mode 100644 index 0000000..b1b8389 Binary files /dev/null and b/static/tiles/12/2475/1285.png differ diff --git a/static/tiles/12/2476/1275.png b/static/tiles/12/2476/1275.png new file mode 100644 index 0000000..6c2e539 Binary files /dev/null and b/static/tiles/12/2476/1275.png differ diff --git a/static/tiles/12/2476/1276.png b/static/tiles/12/2476/1276.png new file mode 100644 index 0000000..6520e2c Binary files /dev/null and b/static/tiles/12/2476/1276.png differ diff --git a/static/tiles/12/2476/1277.png b/static/tiles/12/2476/1277.png new file mode 100644 index 0000000..f398f7f Binary files /dev/null and b/static/tiles/12/2476/1277.png differ diff --git a/static/tiles/12/2476/1278.png b/static/tiles/12/2476/1278.png new file mode 100644 index 0000000..135f454 Binary files /dev/null and b/static/tiles/12/2476/1278.png differ diff --git a/static/tiles/12/2476/1279.png b/static/tiles/12/2476/1279.png new file mode 100644 index 0000000..5d5c95f Binary files /dev/null and b/static/tiles/12/2476/1279.png differ diff --git a/static/tiles/12/2476/1280.png b/static/tiles/12/2476/1280.png new file mode 100644 index 0000000..57128bb Binary files /dev/null and b/static/tiles/12/2476/1280.png differ diff --git a/static/tiles/12/2476/1281.png b/static/tiles/12/2476/1281.png new file mode 100644 index 0000000..f03349e Binary files /dev/null and b/static/tiles/12/2476/1281.png differ diff --git a/static/tiles/12/2476/1282.png b/static/tiles/12/2476/1282.png new file mode 100644 index 0000000..bde20e8 Binary files /dev/null and b/static/tiles/12/2476/1282.png differ diff --git a/static/tiles/12/2476/1283.png b/static/tiles/12/2476/1283.png new file mode 100644 index 0000000..eff54da Binary files /dev/null and b/static/tiles/12/2476/1283.png differ diff --git a/static/tiles/12/2476/1284.png b/static/tiles/12/2476/1284.png new file mode 100644 index 0000000..9c1924b Binary files /dev/null and b/static/tiles/12/2476/1284.png differ diff --git a/static/tiles/12/2476/1285.png b/static/tiles/12/2476/1285.png new file mode 100644 index 0000000..00d9aa8 Binary files /dev/null and b/static/tiles/12/2476/1285.png differ diff --git a/static/tiles/12/2477/1275.png b/static/tiles/12/2477/1275.png new file mode 100644 index 0000000..5a2382f Binary files /dev/null and b/static/tiles/12/2477/1275.png differ diff --git a/static/tiles/12/2477/1276.png b/static/tiles/12/2477/1276.png new file mode 100644 index 0000000..e14db41 Binary files /dev/null and b/static/tiles/12/2477/1276.png differ diff --git a/static/tiles/12/2477/1277.png b/static/tiles/12/2477/1277.png new file mode 100644 index 0000000..9be71e5 Binary files /dev/null and b/static/tiles/12/2477/1277.png differ diff --git a/static/tiles/12/2477/1278.png b/static/tiles/12/2477/1278.png new file mode 100644 index 0000000..e99c875 Binary files /dev/null and b/static/tiles/12/2477/1278.png differ diff --git a/static/tiles/12/2477/1279.png b/static/tiles/12/2477/1279.png new file mode 100644 index 0000000..07b103d Binary files /dev/null and b/static/tiles/12/2477/1279.png differ diff --git a/static/tiles/12/2477/1280.png b/static/tiles/12/2477/1280.png new file mode 100644 index 0000000..fd752aa Binary files /dev/null and b/static/tiles/12/2477/1280.png differ diff --git a/static/tiles/12/2477/1281.png b/static/tiles/12/2477/1281.png new file mode 100644 index 0000000..1ee7b1c Binary files /dev/null and b/static/tiles/12/2477/1281.png differ diff --git a/static/tiles/12/2477/1282.png b/static/tiles/12/2477/1282.png new file mode 100644 index 0000000..8cc6f41 Binary files /dev/null and b/static/tiles/12/2477/1282.png differ diff --git a/static/tiles/12/2477/1283.png b/static/tiles/12/2477/1283.png new file mode 100644 index 0000000..c43d2da Binary files /dev/null and b/static/tiles/12/2477/1283.png differ diff --git a/static/tiles/12/2477/1284.png b/static/tiles/12/2477/1284.png new file mode 100644 index 0000000..383f26d Binary files /dev/null and b/static/tiles/12/2477/1284.png differ diff --git a/static/tiles/12/2477/1285.png b/static/tiles/12/2477/1285.png new file mode 100644 index 0000000..bd898d6 Binary files /dev/null and b/static/tiles/12/2477/1285.png differ diff --git a/static/tiles/12/2478/1275.png b/static/tiles/12/2478/1275.png new file mode 100644 index 0000000..2310055 Binary files /dev/null and b/static/tiles/12/2478/1275.png differ diff --git a/static/tiles/12/2478/1276.png b/static/tiles/12/2478/1276.png new file mode 100644 index 0000000..db72188 Binary files /dev/null and b/static/tiles/12/2478/1276.png differ diff --git a/static/tiles/12/2478/1277.png b/static/tiles/12/2478/1277.png new file mode 100644 index 0000000..411d4e7 Binary files /dev/null and b/static/tiles/12/2478/1277.png differ diff --git a/static/tiles/12/2478/1278.png b/static/tiles/12/2478/1278.png new file mode 100644 index 0000000..8858e0c Binary files /dev/null and b/static/tiles/12/2478/1278.png differ diff --git a/static/tiles/12/2478/1279.png b/static/tiles/12/2478/1279.png new file mode 100644 index 0000000..29fcb1c Binary files /dev/null and b/static/tiles/12/2478/1279.png differ diff --git a/static/tiles/12/2478/1280.png b/static/tiles/12/2478/1280.png new file mode 100644 index 0000000..ce86f52 Binary files /dev/null and b/static/tiles/12/2478/1280.png differ diff --git a/static/tiles/12/2478/1281.png b/static/tiles/12/2478/1281.png new file mode 100644 index 0000000..9b2e953 Binary files /dev/null and b/static/tiles/12/2478/1281.png differ diff --git a/static/tiles/12/2478/1282.png b/static/tiles/12/2478/1282.png new file mode 100644 index 0000000..6135f64 Binary files /dev/null and b/static/tiles/12/2478/1282.png differ diff --git a/static/tiles/12/2478/1283.png b/static/tiles/12/2478/1283.png new file mode 100644 index 0000000..d422f53 Binary files /dev/null and b/static/tiles/12/2478/1283.png differ diff --git a/static/tiles/12/2478/1284.png b/static/tiles/12/2478/1284.png new file mode 100644 index 0000000..c262d30 Binary files /dev/null and b/static/tiles/12/2478/1284.png differ diff --git a/static/tiles/12/2478/1285.png b/static/tiles/12/2478/1285.png new file mode 100644 index 0000000..576638c Binary files /dev/null and b/static/tiles/12/2478/1285.png differ diff --git a/static/tiles/12/2479/1275.png b/static/tiles/12/2479/1275.png new file mode 100644 index 0000000..dbae90c Binary files /dev/null and b/static/tiles/12/2479/1275.png differ diff --git a/static/tiles/12/2479/1276.png b/static/tiles/12/2479/1276.png new file mode 100644 index 0000000..676b00b Binary files /dev/null and b/static/tiles/12/2479/1276.png differ diff --git a/static/tiles/12/2479/1277.png b/static/tiles/12/2479/1277.png new file mode 100644 index 0000000..5dedae3 Binary files /dev/null and b/static/tiles/12/2479/1277.png differ diff --git a/static/tiles/12/2479/1278.png b/static/tiles/12/2479/1278.png new file mode 100644 index 0000000..7ad12fc Binary files /dev/null and b/static/tiles/12/2479/1278.png differ diff --git a/static/tiles/12/2479/1279.png b/static/tiles/12/2479/1279.png new file mode 100644 index 0000000..ceef335 Binary files /dev/null and b/static/tiles/12/2479/1279.png differ diff --git a/static/tiles/12/2479/1280.png b/static/tiles/12/2479/1280.png new file mode 100644 index 0000000..6eebbf1 Binary files /dev/null and b/static/tiles/12/2479/1280.png differ diff --git a/static/tiles/12/2479/1281.png b/static/tiles/12/2479/1281.png new file mode 100644 index 0000000..5c764ed Binary files /dev/null and b/static/tiles/12/2479/1281.png differ diff --git a/static/tiles/12/2479/1282.png b/static/tiles/12/2479/1282.png new file mode 100644 index 0000000..8d868f5 Binary files /dev/null and b/static/tiles/12/2479/1282.png differ diff --git a/static/tiles/12/2479/1283.png b/static/tiles/12/2479/1283.png new file mode 100644 index 0000000..8867235 Binary files /dev/null and b/static/tiles/12/2479/1283.png differ diff --git a/static/tiles/12/2479/1284.png b/static/tiles/12/2479/1284.png new file mode 100644 index 0000000..e9608b1 Binary files /dev/null and b/static/tiles/12/2479/1284.png differ diff --git a/static/tiles/12/2479/1285.png b/static/tiles/12/2479/1285.png new file mode 100644 index 0000000..9a9bfcf Binary files /dev/null and b/static/tiles/12/2479/1285.png differ diff --git a/static/tiles/2/2/1.png b/static/tiles/2/2/1.png new file mode 100644 index 0000000..bdafaa8 Binary files /dev/null and b/static/tiles/2/2/1.png differ diff --git a/static/tiles/3/4/2.png b/static/tiles/3/4/2.png new file mode 100644 index 0000000..970b445 Binary files /dev/null and b/static/tiles/3/4/2.png differ diff --git a/static/tiles/3/4/3.png b/static/tiles/3/4/3.png new file mode 100644 index 0000000..5bed66b Binary files /dev/null and b/static/tiles/3/4/3.png differ diff --git a/static/tiles/3/5/2.png b/static/tiles/3/5/2.png new file mode 100644 index 0000000..9bfa372 Binary files /dev/null and b/static/tiles/3/5/2.png differ diff --git a/static/tiles/3/5/3.png b/static/tiles/3/5/3.png new file mode 100644 index 0000000..6361b61 Binary files /dev/null and b/static/tiles/3/5/3.png differ diff --git a/static/tiles/4/10/4.png b/static/tiles/4/10/4.png new file mode 100644 index 0000000..b29e01a Binary files /dev/null and b/static/tiles/4/10/4.png differ diff --git a/static/tiles/4/10/5.png b/static/tiles/4/10/5.png new file mode 100644 index 0000000..8f5f345 Binary files /dev/null and b/static/tiles/4/10/5.png differ diff --git a/static/tiles/4/10/6.png b/static/tiles/4/10/6.png new file mode 100644 index 0000000..4939806 Binary files /dev/null and b/static/tiles/4/10/6.png differ diff --git a/static/tiles/4/9/4.png b/static/tiles/4/9/4.png new file mode 100644 index 0000000..c92e9be Binary files /dev/null and b/static/tiles/4/9/4.png differ diff --git a/static/tiles/4/9/5.png b/static/tiles/4/9/5.png new file mode 100644 index 0000000..2fc05d7 Binary files /dev/null and b/static/tiles/4/9/5.png differ diff --git a/static/tiles/4/9/6.png b/static/tiles/4/9/6.png new file mode 100644 index 0000000..936c5a9 Binary files /dev/null and b/static/tiles/4/9/6.png differ diff --git a/static/tiles/5/18/10.png b/static/tiles/5/18/10.png new file mode 100644 index 0000000..6ebc961 Binary files /dev/null and b/static/tiles/5/18/10.png differ diff --git a/static/tiles/5/18/11.png b/static/tiles/5/18/11.png new file mode 100644 index 0000000..51702e0 Binary files /dev/null and b/static/tiles/5/18/11.png differ diff --git a/static/tiles/5/18/12.png b/static/tiles/5/18/12.png new file mode 100644 index 0000000..c21e360 Binary files /dev/null and b/static/tiles/5/18/12.png differ diff --git a/static/tiles/5/18/13.png b/static/tiles/5/18/13.png new file mode 100644 index 0000000..111246e Binary files /dev/null and b/static/tiles/5/18/13.png differ diff --git a/static/tiles/5/18/9.png b/static/tiles/5/18/9.png new file mode 100644 index 0000000..c5e25c6 Binary files /dev/null and b/static/tiles/5/18/9.png differ diff --git a/static/tiles/5/19/10.png b/static/tiles/5/19/10.png new file mode 100644 index 0000000..6d3a4c4 Binary files /dev/null and b/static/tiles/5/19/10.png differ diff --git a/static/tiles/5/19/11.png b/static/tiles/5/19/11.png new file mode 100644 index 0000000..b170ef2 Binary files /dev/null and b/static/tiles/5/19/11.png differ diff --git a/static/tiles/5/19/12.png b/static/tiles/5/19/12.png new file mode 100644 index 0000000..7bec1eb Binary files /dev/null and b/static/tiles/5/19/12.png differ diff --git a/static/tiles/5/19/13.png b/static/tiles/5/19/13.png new file mode 100644 index 0000000..8597b04 Binary files /dev/null and b/static/tiles/5/19/13.png differ diff --git a/static/tiles/5/19/9.png b/static/tiles/5/19/9.png new file mode 100644 index 0000000..8c00633 Binary files /dev/null and b/static/tiles/5/19/9.png differ diff --git a/static/tiles/5/20/10.png b/static/tiles/5/20/10.png new file mode 100644 index 0000000..d663da4 Binary files /dev/null and b/static/tiles/5/20/10.png differ diff --git a/static/tiles/5/20/11.png b/static/tiles/5/20/11.png new file mode 100644 index 0000000..f65d128 Binary files /dev/null and b/static/tiles/5/20/11.png differ diff --git a/static/tiles/5/20/12.png b/static/tiles/5/20/12.png new file mode 100644 index 0000000..0d8b6ba Binary files /dev/null and b/static/tiles/5/20/12.png differ diff --git a/static/tiles/5/20/13.png b/static/tiles/5/20/13.png new file mode 100644 index 0000000..2b2e9e2 Binary files /dev/null and b/static/tiles/5/20/13.png differ diff --git a/static/tiles/5/20/9.png b/static/tiles/5/20/9.png new file mode 100644 index 0000000..7f3286f Binary files /dev/null and b/static/tiles/5/20/9.png differ diff --git a/static/tiles/5/21/10.png b/static/tiles/5/21/10.png new file mode 100644 index 0000000..844805a Binary files /dev/null and b/static/tiles/5/21/10.png differ diff --git a/static/tiles/5/21/11.png b/static/tiles/5/21/11.png new file mode 100644 index 0000000..7cbe12b Binary files /dev/null and b/static/tiles/5/21/11.png differ diff --git a/static/tiles/5/21/12.png b/static/tiles/5/21/12.png new file mode 100644 index 0000000..ea7d647 Binary files /dev/null and b/static/tiles/5/21/12.png differ diff --git a/static/tiles/5/21/13.png b/static/tiles/5/21/13.png new file mode 100644 index 0000000..18706a5 Binary files /dev/null and b/static/tiles/5/21/13.png differ diff --git a/static/tiles/5/21/9.png b/static/tiles/5/21/9.png new file mode 100644 index 0000000..635bd32 Binary files /dev/null and b/static/tiles/5/21/9.png differ diff --git a/static/tiles/6/37/18.png b/static/tiles/6/37/18.png new file mode 100644 index 0000000..8abbbcc Binary files /dev/null and b/static/tiles/6/37/18.png differ diff --git a/static/tiles/6/37/19.png b/static/tiles/6/37/19.png new file mode 100644 index 0000000..6271895 Binary files /dev/null and b/static/tiles/6/37/19.png differ diff --git a/static/tiles/6/37/20.png b/static/tiles/6/37/20.png new file mode 100644 index 0000000..79b23d0 Binary files /dev/null and b/static/tiles/6/37/20.png differ diff --git a/static/tiles/6/37/21.png b/static/tiles/6/37/21.png new file mode 100644 index 0000000..2a5709a Binary files /dev/null and b/static/tiles/6/37/21.png differ diff --git a/static/tiles/6/37/22.png b/static/tiles/6/37/22.png new file mode 100644 index 0000000..9c32274 Binary files /dev/null and b/static/tiles/6/37/22.png differ diff --git a/static/tiles/6/37/23.png b/static/tiles/6/37/23.png new file mode 100644 index 0000000..13ec1ed Binary files /dev/null and b/static/tiles/6/37/23.png differ diff --git a/static/tiles/6/37/24.png b/static/tiles/6/37/24.png new file mode 100644 index 0000000..12008d3 Binary files /dev/null and b/static/tiles/6/37/24.png differ diff --git a/static/tiles/6/37/25.png b/static/tiles/6/37/25.png new file mode 100644 index 0000000..a5e6a3d Binary files /dev/null and b/static/tiles/6/37/25.png differ diff --git a/static/tiles/6/37/26.png b/static/tiles/6/37/26.png new file mode 100644 index 0000000..54127ea Binary files /dev/null and b/static/tiles/6/37/26.png differ diff --git a/static/tiles/6/38/18.png b/static/tiles/6/38/18.png new file mode 100644 index 0000000..6a7740c Binary files /dev/null and b/static/tiles/6/38/18.png differ diff --git a/static/tiles/6/38/19.png b/static/tiles/6/38/19.png new file mode 100644 index 0000000..507480e Binary files /dev/null and b/static/tiles/6/38/19.png differ diff --git a/static/tiles/6/38/20.png b/static/tiles/6/38/20.png new file mode 100644 index 0000000..d3965cd Binary files /dev/null and b/static/tiles/6/38/20.png differ diff --git a/static/tiles/6/38/21.png b/static/tiles/6/38/21.png new file mode 100644 index 0000000..d697840 Binary files /dev/null and b/static/tiles/6/38/21.png differ diff --git a/static/tiles/6/38/22.png b/static/tiles/6/38/22.png new file mode 100644 index 0000000..9fee989 Binary files /dev/null and b/static/tiles/6/38/22.png differ diff --git a/static/tiles/6/38/23.png b/static/tiles/6/38/23.png new file mode 100644 index 0000000..bf71242 Binary files /dev/null and b/static/tiles/6/38/23.png differ diff --git a/static/tiles/6/38/24.png b/static/tiles/6/38/24.png new file mode 100644 index 0000000..467839b Binary files /dev/null and b/static/tiles/6/38/24.png differ diff --git a/static/tiles/6/38/25.png b/static/tiles/6/38/25.png new file mode 100644 index 0000000..404138d Binary files /dev/null and b/static/tiles/6/38/25.png differ diff --git a/static/tiles/6/38/26.png b/static/tiles/6/38/26.png new file mode 100644 index 0000000..104b85d Binary files /dev/null and b/static/tiles/6/38/26.png differ diff --git a/static/tiles/6/39/18.png b/static/tiles/6/39/18.png new file mode 100644 index 0000000..9e7f3b8 Binary files /dev/null and b/static/tiles/6/39/18.png differ diff --git a/static/tiles/6/39/19.png b/static/tiles/6/39/19.png new file mode 100644 index 0000000..04c87dc Binary files /dev/null and b/static/tiles/6/39/19.png differ diff --git a/static/tiles/6/39/20.png b/static/tiles/6/39/20.png new file mode 100644 index 0000000..8f6b0ba Binary files /dev/null and b/static/tiles/6/39/20.png differ diff --git a/static/tiles/6/39/21.png b/static/tiles/6/39/21.png new file mode 100644 index 0000000..071fc9a Binary files /dev/null and b/static/tiles/6/39/21.png differ diff --git a/static/tiles/6/39/22.png b/static/tiles/6/39/22.png new file mode 100644 index 0000000..e59b16c Binary files /dev/null and b/static/tiles/6/39/22.png differ diff --git a/static/tiles/6/39/23.png b/static/tiles/6/39/23.png new file mode 100644 index 0000000..1419dc9 Binary files /dev/null and b/static/tiles/6/39/23.png differ diff --git a/static/tiles/6/39/24.png b/static/tiles/6/39/24.png new file mode 100644 index 0000000..f173995 Binary files /dev/null and b/static/tiles/6/39/24.png differ diff --git a/static/tiles/6/39/25.png b/static/tiles/6/39/25.png new file mode 100644 index 0000000..82700c1 Binary files /dev/null and b/static/tiles/6/39/25.png differ diff --git a/static/tiles/6/39/26.png b/static/tiles/6/39/26.png new file mode 100644 index 0000000..a848ee5 Binary files /dev/null and b/static/tiles/6/39/26.png differ diff --git a/static/tiles/6/40/18.png b/static/tiles/6/40/18.png new file mode 100644 index 0000000..86939b2 Binary files /dev/null and b/static/tiles/6/40/18.png differ diff --git a/static/tiles/6/40/19.png b/static/tiles/6/40/19.png new file mode 100644 index 0000000..525e453 Binary files /dev/null and b/static/tiles/6/40/19.png differ diff --git a/static/tiles/6/40/20.png b/static/tiles/6/40/20.png new file mode 100644 index 0000000..8603c41 Binary files /dev/null and b/static/tiles/6/40/20.png differ diff --git a/static/tiles/6/40/21.png b/static/tiles/6/40/21.png new file mode 100644 index 0000000..c815a32 Binary files /dev/null and b/static/tiles/6/40/21.png differ diff --git a/static/tiles/6/40/22.png b/static/tiles/6/40/22.png new file mode 100644 index 0000000..dc94079 Binary files /dev/null and b/static/tiles/6/40/22.png differ diff --git a/static/tiles/6/40/23.png b/static/tiles/6/40/23.png new file mode 100644 index 0000000..c362e90 Binary files /dev/null and b/static/tiles/6/40/23.png differ diff --git a/static/tiles/6/40/24.png b/static/tiles/6/40/24.png new file mode 100644 index 0000000..6ae7b25 Binary files /dev/null and b/static/tiles/6/40/24.png differ diff --git a/static/tiles/6/40/25.png b/static/tiles/6/40/25.png new file mode 100644 index 0000000..02802b3 Binary files /dev/null and b/static/tiles/6/40/25.png differ diff --git a/static/tiles/6/40/26.png b/static/tiles/6/40/26.png new file mode 100644 index 0000000..78f81fe Binary files /dev/null and b/static/tiles/6/40/26.png differ diff --git a/static/tiles/6/41/18.png b/static/tiles/6/41/18.png new file mode 100644 index 0000000..7079af2 Binary files /dev/null and b/static/tiles/6/41/18.png differ diff --git a/static/tiles/6/41/19.png b/static/tiles/6/41/19.png new file mode 100644 index 0000000..2c89959 Binary files /dev/null and b/static/tiles/6/41/19.png differ diff --git a/static/tiles/6/41/20.png b/static/tiles/6/41/20.png new file mode 100644 index 0000000..84e814e Binary files /dev/null and b/static/tiles/6/41/20.png differ diff --git a/static/tiles/6/41/21.png b/static/tiles/6/41/21.png new file mode 100644 index 0000000..de25ccd Binary files /dev/null and b/static/tiles/6/41/21.png differ diff --git a/static/tiles/6/41/22.png b/static/tiles/6/41/22.png new file mode 100644 index 0000000..dfad791 Binary files /dev/null and b/static/tiles/6/41/22.png differ diff --git a/static/tiles/6/41/23.png b/static/tiles/6/41/23.png new file mode 100644 index 0000000..aa3b063 Binary files /dev/null and b/static/tiles/6/41/23.png differ diff --git a/static/tiles/6/41/24.png b/static/tiles/6/41/24.png new file mode 100644 index 0000000..04da50b Binary files /dev/null and b/static/tiles/6/41/24.png differ diff --git a/static/tiles/6/41/25.png b/static/tiles/6/41/25.png new file mode 100644 index 0000000..a3ac717 Binary files /dev/null and b/static/tiles/6/41/25.png differ diff --git a/static/tiles/6/41/26.png b/static/tiles/6/41/26.png new file mode 100644 index 0000000..619b74e Binary files /dev/null and b/static/tiles/6/41/26.png differ diff --git a/static/tiles/6/42/18.png b/static/tiles/6/42/18.png new file mode 100644 index 0000000..95d1257 Binary files /dev/null and b/static/tiles/6/42/18.png differ diff --git a/static/tiles/6/42/19.png b/static/tiles/6/42/19.png new file mode 100644 index 0000000..455860a Binary files /dev/null and b/static/tiles/6/42/19.png differ diff --git a/static/tiles/6/42/20.png b/static/tiles/6/42/20.png new file mode 100644 index 0000000..b98d0d3 Binary files /dev/null and b/static/tiles/6/42/20.png differ diff --git a/static/tiles/6/42/21.png b/static/tiles/6/42/21.png new file mode 100644 index 0000000..4321b00 Binary files /dev/null and b/static/tiles/6/42/21.png differ diff --git a/static/tiles/6/42/22.png b/static/tiles/6/42/22.png new file mode 100644 index 0000000..ed7b7b0 Binary files /dev/null and b/static/tiles/6/42/22.png differ diff --git a/static/tiles/6/42/23.png b/static/tiles/6/42/23.png new file mode 100644 index 0000000..92a89bc Binary files /dev/null and b/static/tiles/6/42/23.png differ diff --git a/static/tiles/6/42/24.png b/static/tiles/6/42/24.png new file mode 100644 index 0000000..5bc9d0b Binary files /dev/null and b/static/tiles/6/42/24.png differ diff --git a/static/tiles/6/42/25.png b/static/tiles/6/42/25.png new file mode 100644 index 0000000..6be759c Binary files /dev/null and b/static/tiles/6/42/25.png differ diff --git a/static/tiles/6/42/26.png b/static/tiles/6/42/26.png new file mode 100644 index 0000000..78057fb Binary files /dev/null and b/static/tiles/6/42/26.png differ diff --git a/static/tiles/6/43/18.png b/static/tiles/6/43/18.png new file mode 100644 index 0000000..3e26e80 Binary files /dev/null and b/static/tiles/6/43/18.png differ diff --git a/static/tiles/6/43/19.png b/static/tiles/6/43/19.png new file mode 100644 index 0000000..c13e63b Binary files /dev/null and b/static/tiles/6/43/19.png differ diff --git a/static/tiles/6/43/20.png b/static/tiles/6/43/20.png new file mode 100644 index 0000000..533db34 Binary files /dev/null and b/static/tiles/6/43/20.png differ diff --git a/static/tiles/6/43/21.png b/static/tiles/6/43/21.png new file mode 100644 index 0000000..76a0554 Binary files /dev/null and b/static/tiles/6/43/21.png differ diff --git a/static/tiles/6/43/22.png b/static/tiles/6/43/22.png new file mode 100644 index 0000000..3de360d Binary files /dev/null and b/static/tiles/6/43/22.png differ diff --git a/static/tiles/6/43/23.png b/static/tiles/6/43/23.png new file mode 100644 index 0000000..26bda29 Binary files /dev/null and b/static/tiles/6/43/23.png differ diff --git a/static/tiles/6/43/24.png b/static/tiles/6/43/24.png new file mode 100644 index 0000000..67836b6 Binary files /dev/null and b/static/tiles/6/43/24.png differ diff --git a/static/tiles/6/43/25.png b/static/tiles/6/43/25.png new file mode 100644 index 0000000..0dcfe26 Binary files /dev/null and b/static/tiles/6/43/25.png differ diff --git a/static/tiles/6/43/26.png b/static/tiles/6/43/26.png new file mode 100644 index 0000000..260d2a7 Binary files /dev/null and b/static/tiles/6/43/26.png differ diff --git a/static/tiles/7/75/37.png b/static/tiles/7/75/37.png new file mode 100644 index 0000000..0204105 Binary files /dev/null and b/static/tiles/7/75/37.png differ diff --git a/static/tiles/7/75/38.png b/static/tiles/7/75/38.png new file mode 100644 index 0000000..ef157bb Binary files /dev/null and b/static/tiles/7/75/38.png differ diff --git a/static/tiles/7/75/39.png b/static/tiles/7/75/39.png new file mode 100644 index 0000000..11fda31 Binary files /dev/null and b/static/tiles/7/75/39.png differ diff --git a/static/tiles/7/75/40.png b/static/tiles/7/75/40.png new file mode 100644 index 0000000..71624e5 Binary files /dev/null and b/static/tiles/7/75/40.png differ diff --git a/static/tiles/7/75/41.png b/static/tiles/7/75/41.png new file mode 100644 index 0000000..709fe12 Binary files /dev/null and b/static/tiles/7/75/41.png differ diff --git a/static/tiles/7/75/42.png b/static/tiles/7/75/42.png new file mode 100644 index 0000000..9982c8a Binary files /dev/null and b/static/tiles/7/75/42.png differ diff --git a/static/tiles/7/75/43.png b/static/tiles/7/75/43.png new file mode 100644 index 0000000..79b55c2 Binary files /dev/null and b/static/tiles/7/75/43.png differ diff --git a/static/tiles/7/75/44.png b/static/tiles/7/75/44.png new file mode 100644 index 0000000..c1a7efc Binary files /dev/null and b/static/tiles/7/75/44.png differ diff --git a/static/tiles/7/75/45.png b/static/tiles/7/75/45.png new file mode 100644 index 0000000..7776eef Binary files /dev/null and b/static/tiles/7/75/45.png differ diff --git a/static/tiles/7/75/46.png b/static/tiles/7/75/46.png new file mode 100644 index 0000000..092eb9c Binary files /dev/null and b/static/tiles/7/75/46.png differ diff --git a/static/tiles/7/75/47.png b/static/tiles/7/75/47.png new file mode 100644 index 0000000..eb55126 Binary files /dev/null and b/static/tiles/7/75/47.png differ diff --git a/static/tiles/7/75/48.png b/static/tiles/7/75/48.png new file mode 100644 index 0000000..851dc68 Binary files /dev/null and b/static/tiles/7/75/48.png differ diff --git a/static/tiles/7/75/49.png b/static/tiles/7/75/49.png new file mode 100644 index 0000000..31a1eb6 Binary files /dev/null and b/static/tiles/7/75/49.png differ diff --git a/static/tiles/7/75/50.png b/static/tiles/7/75/50.png new file mode 100644 index 0000000..92f5522 Binary files /dev/null and b/static/tiles/7/75/50.png differ diff --git a/static/tiles/7/75/51.png b/static/tiles/7/75/51.png new file mode 100644 index 0000000..f607ae0 Binary files /dev/null and b/static/tiles/7/75/51.png differ diff --git a/static/tiles/7/75/52.png b/static/tiles/7/75/52.png new file mode 100644 index 0000000..41af698 Binary files /dev/null and b/static/tiles/7/75/52.png differ diff --git a/static/tiles/7/75/53.png b/static/tiles/7/75/53.png new file mode 100644 index 0000000..4a92b16 Binary files /dev/null and b/static/tiles/7/75/53.png differ diff --git a/static/tiles/7/76/37.png b/static/tiles/7/76/37.png new file mode 100644 index 0000000..d508c7e Binary files /dev/null and b/static/tiles/7/76/37.png differ diff --git a/static/tiles/7/76/38.png b/static/tiles/7/76/38.png new file mode 100644 index 0000000..8d423a8 Binary files /dev/null and b/static/tiles/7/76/38.png differ diff --git a/static/tiles/7/76/39.png b/static/tiles/7/76/39.png new file mode 100644 index 0000000..a9821e8 Binary files /dev/null and b/static/tiles/7/76/39.png differ diff --git a/static/tiles/7/76/40.png b/static/tiles/7/76/40.png new file mode 100644 index 0000000..548b292 Binary files /dev/null and b/static/tiles/7/76/40.png differ diff --git a/static/tiles/7/76/41.png b/static/tiles/7/76/41.png new file mode 100644 index 0000000..b2b4e95 Binary files /dev/null and b/static/tiles/7/76/41.png differ diff --git a/static/tiles/7/76/42.png b/static/tiles/7/76/42.png new file mode 100644 index 0000000..43b9d97 Binary files /dev/null and b/static/tiles/7/76/42.png differ diff --git a/static/tiles/7/76/43.png b/static/tiles/7/76/43.png new file mode 100644 index 0000000..e8562cb Binary files /dev/null and b/static/tiles/7/76/43.png differ diff --git a/static/tiles/7/76/44.png b/static/tiles/7/76/44.png new file mode 100644 index 0000000..c22ade0 Binary files /dev/null and b/static/tiles/7/76/44.png differ diff --git a/static/tiles/7/76/45.png b/static/tiles/7/76/45.png new file mode 100644 index 0000000..d2458ab Binary files /dev/null and b/static/tiles/7/76/45.png differ diff --git a/static/tiles/7/76/46.png b/static/tiles/7/76/46.png new file mode 100644 index 0000000..daacf19 Binary files /dev/null and b/static/tiles/7/76/46.png differ diff --git a/static/tiles/7/76/47.png b/static/tiles/7/76/47.png new file mode 100644 index 0000000..e08c019 Binary files /dev/null and b/static/tiles/7/76/47.png differ diff --git a/static/tiles/7/76/48.png b/static/tiles/7/76/48.png new file mode 100644 index 0000000..9cb3fc2 Binary files /dev/null and b/static/tiles/7/76/48.png differ diff --git a/static/tiles/7/76/49.png b/static/tiles/7/76/49.png new file mode 100644 index 0000000..a2c8f39 Binary files /dev/null and b/static/tiles/7/76/49.png differ diff --git a/static/tiles/7/76/50.png b/static/tiles/7/76/50.png new file mode 100644 index 0000000..5aac3c7 Binary files /dev/null and b/static/tiles/7/76/50.png differ diff --git a/static/tiles/7/76/51.png b/static/tiles/7/76/51.png new file mode 100644 index 0000000..eb73493 Binary files /dev/null and b/static/tiles/7/76/51.png differ diff --git a/static/tiles/7/76/52.png b/static/tiles/7/76/52.png new file mode 100644 index 0000000..fff6690 Binary files /dev/null and b/static/tiles/7/76/52.png differ diff --git a/static/tiles/7/76/53.png b/static/tiles/7/76/53.png new file mode 100644 index 0000000..4fc0ad4 Binary files /dev/null and b/static/tiles/7/76/53.png differ diff --git a/static/tiles/7/77/37.png b/static/tiles/7/77/37.png new file mode 100644 index 0000000..f05a758 Binary files /dev/null and b/static/tiles/7/77/37.png differ diff --git a/static/tiles/7/77/38.png b/static/tiles/7/77/38.png new file mode 100644 index 0000000..260f0fc Binary files /dev/null and b/static/tiles/7/77/38.png differ diff --git a/static/tiles/7/77/39.png b/static/tiles/7/77/39.png new file mode 100644 index 0000000..0ea08cd Binary files /dev/null and b/static/tiles/7/77/39.png differ diff --git a/static/tiles/7/77/40.png b/static/tiles/7/77/40.png new file mode 100644 index 0000000..79393e0 Binary files /dev/null and b/static/tiles/7/77/40.png differ diff --git a/static/tiles/7/77/41.png b/static/tiles/7/77/41.png new file mode 100644 index 0000000..430aec9 Binary files /dev/null and b/static/tiles/7/77/41.png differ diff --git a/static/tiles/7/77/42.png b/static/tiles/7/77/42.png new file mode 100644 index 0000000..2f7adc6 Binary files /dev/null and b/static/tiles/7/77/42.png differ diff --git a/static/tiles/7/77/43.png b/static/tiles/7/77/43.png new file mode 100644 index 0000000..f736ee9 Binary files /dev/null and b/static/tiles/7/77/43.png differ diff --git a/static/tiles/7/77/44.png b/static/tiles/7/77/44.png new file mode 100644 index 0000000..4f36f62 Binary files /dev/null and b/static/tiles/7/77/44.png differ diff --git a/static/tiles/7/77/45.png b/static/tiles/7/77/45.png new file mode 100644 index 0000000..6a1a706 Binary files /dev/null and b/static/tiles/7/77/45.png differ diff --git a/static/tiles/7/77/46.png b/static/tiles/7/77/46.png new file mode 100644 index 0000000..a91c396 Binary files /dev/null and b/static/tiles/7/77/46.png differ diff --git a/static/tiles/7/77/47.png b/static/tiles/7/77/47.png new file mode 100644 index 0000000..efbc891 Binary files /dev/null and b/static/tiles/7/77/47.png differ diff --git a/static/tiles/7/77/48.png b/static/tiles/7/77/48.png new file mode 100644 index 0000000..f9afb7f Binary files /dev/null and b/static/tiles/7/77/48.png differ diff --git a/static/tiles/7/77/49.png b/static/tiles/7/77/49.png new file mode 100644 index 0000000..c3d72b6 Binary files /dev/null and b/static/tiles/7/77/49.png differ diff --git a/static/tiles/7/77/50.png b/static/tiles/7/77/50.png new file mode 100644 index 0000000..6bc9d91 Binary files /dev/null and b/static/tiles/7/77/50.png differ diff --git a/static/tiles/7/77/51.png b/static/tiles/7/77/51.png new file mode 100644 index 0000000..8f4b1b0 Binary files /dev/null and b/static/tiles/7/77/51.png differ diff --git a/static/tiles/7/77/52.png b/static/tiles/7/77/52.png new file mode 100644 index 0000000..317c892 Binary files /dev/null and b/static/tiles/7/77/52.png differ diff --git a/static/tiles/7/77/53.png b/static/tiles/7/77/53.png new file mode 100644 index 0000000..c313e61 Binary files /dev/null and b/static/tiles/7/77/53.png differ diff --git a/static/tiles/7/78/37.png b/static/tiles/7/78/37.png new file mode 100644 index 0000000..7fa6df2 Binary files /dev/null and b/static/tiles/7/78/37.png differ diff --git a/static/tiles/7/78/38.png b/static/tiles/7/78/38.png new file mode 100644 index 0000000..c790efd Binary files /dev/null and b/static/tiles/7/78/38.png differ diff --git a/static/tiles/7/78/39.png b/static/tiles/7/78/39.png new file mode 100644 index 0000000..21d0cf2 Binary files /dev/null and b/static/tiles/7/78/39.png differ diff --git a/static/tiles/7/78/40.png b/static/tiles/7/78/40.png new file mode 100644 index 0000000..4bfd4fb Binary files /dev/null and b/static/tiles/7/78/40.png differ diff --git a/static/tiles/7/78/41.png b/static/tiles/7/78/41.png new file mode 100644 index 0000000..2fca861 Binary files /dev/null and b/static/tiles/7/78/41.png differ diff --git a/static/tiles/7/78/42.png b/static/tiles/7/78/42.png new file mode 100644 index 0000000..9ed69c5 Binary files /dev/null and b/static/tiles/7/78/42.png differ diff --git a/static/tiles/7/78/43.png b/static/tiles/7/78/43.png new file mode 100644 index 0000000..2001140 Binary files /dev/null and b/static/tiles/7/78/43.png differ diff --git a/static/tiles/7/78/44.png b/static/tiles/7/78/44.png new file mode 100644 index 0000000..67cf3cf Binary files /dev/null and b/static/tiles/7/78/44.png differ diff --git a/static/tiles/7/78/45.png b/static/tiles/7/78/45.png new file mode 100644 index 0000000..7c62cc8 Binary files /dev/null and b/static/tiles/7/78/45.png differ diff --git a/static/tiles/7/78/46.png b/static/tiles/7/78/46.png new file mode 100644 index 0000000..c6485c4 Binary files /dev/null and b/static/tiles/7/78/46.png differ diff --git a/static/tiles/7/78/47.png b/static/tiles/7/78/47.png new file mode 100644 index 0000000..ff8049f Binary files /dev/null and b/static/tiles/7/78/47.png differ diff --git a/static/tiles/7/78/48.png b/static/tiles/7/78/48.png new file mode 100644 index 0000000..5c429ec Binary files /dev/null and b/static/tiles/7/78/48.png differ diff --git a/static/tiles/7/78/49.png b/static/tiles/7/78/49.png new file mode 100644 index 0000000..1564ba6 Binary files /dev/null and b/static/tiles/7/78/49.png differ diff --git a/static/tiles/7/78/50.png b/static/tiles/7/78/50.png new file mode 100644 index 0000000..1463f5b Binary files /dev/null and b/static/tiles/7/78/50.png differ diff --git a/static/tiles/7/78/51.png b/static/tiles/7/78/51.png new file mode 100644 index 0000000..1868545 Binary files /dev/null and b/static/tiles/7/78/51.png differ diff --git a/static/tiles/7/78/52.png b/static/tiles/7/78/52.png new file mode 100644 index 0000000..cbb8e2d Binary files /dev/null and b/static/tiles/7/78/52.png differ diff --git a/static/tiles/7/78/53.png b/static/tiles/7/78/53.png new file mode 100644 index 0000000..a43acd0 Binary files /dev/null and b/static/tiles/7/78/53.png differ diff --git a/static/tiles/7/79/37.png b/static/tiles/7/79/37.png new file mode 100644 index 0000000..ff3a508 Binary files /dev/null and b/static/tiles/7/79/37.png differ diff --git a/static/tiles/7/79/38.png b/static/tiles/7/79/38.png new file mode 100644 index 0000000..4e6a5c9 Binary files /dev/null and b/static/tiles/7/79/38.png differ diff --git a/static/tiles/7/79/39.png b/static/tiles/7/79/39.png new file mode 100644 index 0000000..0658b3c Binary files /dev/null and b/static/tiles/7/79/39.png differ diff --git a/static/tiles/7/79/40.png b/static/tiles/7/79/40.png new file mode 100644 index 0000000..3b5f874 Binary files /dev/null and b/static/tiles/7/79/40.png differ diff --git a/static/tiles/7/79/41.png b/static/tiles/7/79/41.png new file mode 100644 index 0000000..0ed04c6 Binary files /dev/null and b/static/tiles/7/79/41.png differ diff --git a/static/tiles/7/79/42.png b/static/tiles/7/79/42.png new file mode 100644 index 0000000..24c7819 Binary files /dev/null and b/static/tiles/7/79/42.png differ diff --git a/static/tiles/7/79/43.png b/static/tiles/7/79/43.png new file mode 100644 index 0000000..64640ba Binary files /dev/null and b/static/tiles/7/79/43.png differ diff --git a/static/tiles/7/79/44.png b/static/tiles/7/79/44.png new file mode 100644 index 0000000..c7e3da1 Binary files /dev/null and b/static/tiles/7/79/44.png differ diff --git a/static/tiles/7/79/45.png b/static/tiles/7/79/45.png new file mode 100644 index 0000000..c79728b Binary files /dev/null and b/static/tiles/7/79/45.png differ diff --git a/static/tiles/7/79/46.png b/static/tiles/7/79/46.png new file mode 100644 index 0000000..f921ba4 Binary files /dev/null and b/static/tiles/7/79/46.png differ diff --git a/static/tiles/7/79/47.png b/static/tiles/7/79/47.png new file mode 100644 index 0000000..905166b Binary files /dev/null and b/static/tiles/7/79/47.png differ diff --git a/static/tiles/7/79/48.png b/static/tiles/7/79/48.png new file mode 100644 index 0000000..c3e0501 Binary files /dev/null and b/static/tiles/7/79/48.png differ diff --git a/static/tiles/7/79/49.png b/static/tiles/7/79/49.png new file mode 100644 index 0000000..65450c2 Binary files /dev/null and b/static/tiles/7/79/49.png differ diff --git a/static/tiles/7/79/50.png b/static/tiles/7/79/50.png new file mode 100644 index 0000000..ffa7a91 Binary files /dev/null and b/static/tiles/7/79/50.png differ diff --git a/static/tiles/7/79/51.png b/static/tiles/7/79/51.png new file mode 100644 index 0000000..b4e7ec5 Binary files /dev/null and b/static/tiles/7/79/51.png differ diff --git a/static/tiles/7/79/52.png b/static/tiles/7/79/52.png new file mode 100644 index 0000000..c5ef49d Binary files /dev/null and b/static/tiles/7/79/52.png differ diff --git a/static/tiles/7/79/53.png b/static/tiles/7/79/53.png new file mode 100644 index 0000000..33ae02e Binary files /dev/null and b/static/tiles/7/79/53.png differ diff --git a/static/tiles/7/80/37.png b/static/tiles/7/80/37.png new file mode 100644 index 0000000..f4bd32f Binary files /dev/null and b/static/tiles/7/80/37.png differ diff --git a/static/tiles/7/80/38.png b/static/tiles/7/80/38.png new file mode 100644 index 0000000..8be16bc Binary files /dev/null and b/static/tiles/7/80/38.png differ diff --git a/static/tiles/7/80/39.png b/static/tiles/7/80/39.png new file mode 100644 index 0000000..3eafc0b Binary files /dev/null and b/static/tiles/7/80/39.png differ diff --git a/static/tiles/7/80/40.png b/static/tiles/7/80/40.png new file mode 100644 index 0000000..b1bc5d5 Binary files /dev/null and b/static/tiles/7/80/40.png differ diff --git a/static/tiles/7/80/41.png b/static/tiles/7/80/41.png new file mode 100644 index 0000000..3491370 Binary files /dev/null and b/static/tiles/7/80/41.png differ diff --git a/static/tiles/7/80/42.png b/static/tiles/7/80/42.png new file mode 100644 index 0000000..7df1b7a Binary files /dev/null and b/static/tiles/7/80/42.png differ diff --git a/static/tiles/7/80/43.png b/static/tiles/7/80/43.png new file mode 100644 index 0000000..6a2747a Binary files /dev/null and b/static/tiles/7/80/43.png differ diff --git a/static/tiles/7/80/44.png b/static/tiles/7/80/44.png new file mode 100644 index 0000000..d1ff847 Binary files /dev/null and b/static/tiles/7/80/44.png differ diff --git a/static/tiles/7/80/45.png b/static/tiles/7/80/45.png new file mode 100644 index 0000000..c4ca116 Binary files /dev/null and b/static/tiles/7/80/45.png differ diff --git a/static/tiles/7/80/46.png b/static/tiles/7/80/46.png new file mode 100644 index 0000000..925b213 Binary files /dev/null and b/static/tiles/7/80/46.png differ diff --git a/static/tiles/7/80/47.png b/static/tiles/7/80/47.png new file mode 100644 index 0000000..ea1d5f1 Binary files /dev/null and b/static/tiles/7/80/47.png differ diff --git a/static/tiles/7/80/48.png b/static/tiles/7/80/48.png new file mode 100644 index 0000000..7680743 Binary files /dev/null and b/static/tiles/7/80/48.png differ diff --git a/static/tiles/7/80/49.png b/static/tiles/7/80/49.png new file mode 100644 index 0000000..511b084 Binary files /dev/null and b/static/tiles/7/80/49.png differ diff --git a/static/tiles/7/80/50.png b/static/tiles/7/80/50.png new file mode 100644 index 0000000..3ef3869 Binary files /dev/null and b/static/tiles/7/80/50.png differ diff --git a/static/tiles/7/80/51.png b/static/tiles/7/80/51.png new file mode 100644 index 0000000..150700a Binary files /dev/null and b/static/tiles/7/80/51.png differ diff --git a/static/tiles/7/80/52.png b/static/tiles/7/80/52.png new file mode 100644 index 0000000..a997906 Binary files /dev/null and b/static/tiles/7/80/52.png differ diff --git a/static/tiles/7/80/53.png b/static/tiles/7/80/53.png new file mode 100644 index 0000000..552c722 Binary files /dev/null and b/static/tiles/7/80/53.png differ diff --git a/static/tiles/7/81/37.png b/static/tiles/7/81/37.png new file mode 100644 index 0000000..1076317 Binary files /dev/null and b/static/tiles/7/81/37.png differ diff --git a/static/tiles/7/81/38.png b/static/tiles/7/81/38.png new file mode 100644 index 0000000..17286b1 Binary files /dev/null and b/static/tiles/7/81/38.png differ diff --git a/static/tiles/7/81/39.png b/static/tiles/7/81/39.png new file mode 100644 index 0000000..6b192ab Binary files /dev/null and b/static/tiles/7/81/39.png differ diff --git a/static/tiles/7/81/40.png b/static/tiles/7/81/40.png new file mode 100644 index 0000000..c00ba6e Binary files /dev/null and b/static/tiles/7/81/40.png differ diff --git a/static/tiles/7/81/41.png b/static/tiles/7/81/41.png new file mode 100644 index 0000000..0870c17 Binary files /dev/null and b/static/tiles/7/81/41.png differ diff --git a/static/tiles/7/81/42.png b/static/tiles/7/81/42.png new file mode 100644 index 0000000..66197ad Binary files /dev/null and b/static/tiles/7/81/42.png differ diff --git a/static/tiles/7/81/43.png b/static/tiles/7/81/43.png new file mode 100644 index 0000000..08e7dc4 Binary files /dev/null and b/static/tiles/7/81/43.png differ diff --git a/static/tiles/7/81/44.png b/static/tiles/7/81/44.png new file mode 100644 index 0000000..492d8bc Binary files /dev/null and b/static/tiles/7/81/44.png differ diff --git a/static/tiles/7/81/45.png b/static/tiles/7/81/45.png new file mode 100644 index 0000000..a07c193 Binary files /dev/null and b/static/tiles/7/81/45.png differ diff --git a/static/tiles/7/81/46.png b/static/tiles/7/81/46.png new file mode 100644 index 0000000..585189f Binary files /dev/null and b/static/tiles/7/81/46.png differ diff --git a/static/tiles/7/81/47.png b/static/tiles/7/81/47.png new file mode 100644 index 0000000..f77ab76 Binary files /dev/null and b/static/tiles/7/81/47.png differ diff --git a/static/tiles/7/81/48.png b/static/tiles/7/81/48.png new file mode 100644 index 0000000..db76015 Binary files /dev/null and b/static/tiles/7/81/48.png differ diff --git a/static/tiles/7/81/49.png b/static/tiles/7/81/49.png new file mode 100644 index 0000000..94fb0ec Binary files /dev/null and b/static/tiles/7/81/49.png differ diff --git a/static/tiles/7/81/50.png b/static/tiles/7/81/50.png new file mode 100644 index 0000000..2134927 Binary files /dev/null and b/static/tiles/7/81/50.png differ diff --git a/static/tiles/7/81/51.png b/static/tiles/7/81/51.png new file mode 100644 index 0000000..b4edb05 Binary files /dev/null and b/static/tiles/7/81/51.png differ diff --git a/static/tiles/7/81/52.png b/static/tiles/7/81/52.png new file mode 100644 index 0000000..7fae8c7 Binary files /dev/null and b/static/tiles/7/81/52.png differ diff --git a/static/tiles/7/81/53.png b/static/tiles/7/81/53.png new file mode 100644 index 0000000..1149348 Binary files /dev/null and b/static/tiles/7/81/53.png differ diff --git a/static/tiles/7/82/37.png b/static/tiles/7/82/37.png new file mode 100644 index 0000000..8774cfb Binary files /dev/null and b/static/tiles/7/82/37.png differ diff --git a/static/tiles/7/82/38.png b/static/tiles/7/82/38.png new file mode 100644 index 0000000..31b0749 Binary files /dev/null and b/static/tiles/7/82/38.png differ diff --git a/static/tiles/7/82/39.png b/static/tiles/7/82/39.png new file mode 100644 index 0000000..a0715a5 Binary files /dev/null and b/static/tiles/7/82/39.png differ diff --git a/static/tiles/7/82/40.png b/static/tiles/7/82/40.png new file mode 100644 index 0000000..3c10efe Binary files /dev/null and b/static/tiles/7/82/40.png differ diff --git a/static/tiles/7/82/41.png b/static/tiles/7/82/41.png new file mode 100644 index 0000000..1cbe146 Binary files /dev/null and b/static/tiles/7/82/41.png differ diff --git a/static/tiles/7/82/42.png b/static/tiles/7/82/42.png new file mode 100644 index 0000000..0958751 Binary files /dev/null and b/static/tiles/7/82/42.png differ diff --git a/static/tiles/7/82/43.png b/static/tiles/7/82/43.png new file mode 100644 index 0000000..35ac017 Binary files /dev/null and b/static/tiles/7/82/43.png differ diff --git a/static/tiles/7/82/44.png b/static/tiles/7/82/44.png new file mode 100644 index 0000000..39895ca Binary files /dev/null and b/static/tiles/7/82/44.png differ diff --git a/static/tiles/7/82/45.png b/static/tiles/7/82/45.png new file mode 100644 index 0000000..57931b9 Binary files /dev/null and b/static/tiles/7/82/45.png differ diff --git a/static/tiles/7/82/46.png b/static/tiles/7/82/46.png new file mode 100644 index 0000000..7c6d016 Binary files /dev/null and b/static/tiles/7/82/46.png differ diff --git a/static/tiles/7/82/47.png b/static/tiles/7/82/47.png new file mode 100644 index 0000000..c63fd0f Binary files /dev/null and b/static/tiles/7/82/47.png differ diff --git a/static/tiles/7/82/48.png b/static/tiles/7/82/48.png new file mode 100644 index 0000000..65b8bf2 Binary files /dev/null and b/static/tiles/7/82/48.png differ diff --git a/static/tiles/7/82/49.png b/static/tiles/7/82/49.png new file mode 100644 index 0000000..efe78e6 Binary files /dev/null and b/static/tiles/7/82/49.png differ diff --git a/static/tiles/7/82/50.png b/static/tiles/7/82/50.png new file mode 100644 index 0000000..c8bb20f Binary files /dev/null and b/static/tiles/7/82/50.png differ diff --git a/static/tiles/7/82/51.png b/static/tiles/7/82/51.png new file mode 100644 index 0000000..3023192 Binary files /dev/null and b/static/tiles/7/82/51.png differ diff --git a/static/tiles/7/82/52.png b/static/tiles/7/82/52.png new file mode 100644 index 0000000..849e6d2 Binary files /dev/null and b/static/tiles/7/82/52.png differ diff --git a/static/tiles/7/82/53.png b/static/tiles/7/82/53.png new file mode 100644 index 0000000..165d3da Binary files /dev/null and b/static/tiles/7/82/53.png differ diff --git a/static/tiles/7/83/37.png b/static/tiles/7/83/37.png new file mode 100644 index 0000000..afd7e12 Binary files /dev/null and b/static/tiles/7/83/37.png differ diff --git a/static/tiles/7/83/38.png b/static/tiles/7/83/38.png new file mode 100644 index 0000000..722aff5 Binary files /dev/null and b/static/tiles/7/83/38.png differ diff --git a/static/tiles/7/83/39.png b/static/tiles/7/83/39.png new file mode 100644 index 0000000..a335f35 Binary files /dev/null and b/static/tiles/7/83/39.png differ diff --git a/static/tiles/7/83/40.png b/static/tiles/7/83/40.png new file mode 100644 index 0000000..7cbc395 Binary files /dev/null and b/static/tiles/7/83/40.png differ diff --git a/static/tiles/7/83/41.png b/static/tiles/7/83/41.png new file mode 100644 index 0000000..4ebf1b6 Binary files /dev/null and b/static/tiles/7/83/41.png differ diff --git a/static/tiles/7/83/42.png b/static/tiles/7/83/42.png new file mode 100644 index 0000000..cb2aa2d Binary files /dev/null and b/static/tiles/7/83/42.png differ diff --git a/static/tiles/7/83/43.png b/static/tiles/7/83/43.png new file mode 100644 index 0000000..aa83aa1 Binary files /dev/null and b/static/tiles/7/83/43.png differ diff --git a/static/tiles/7/83/44.png b/static/tiles/7/83/44.png new file mode 100644 index 0000000..395a8f0 Binary files /dev/null and b/static/tiles/7/83/44.png differ diff --git a/static/tiles/7/83/45.png b/static/tiles/7/83/45.png new file mode 100644 index 0000000..a345c77 Binary files /dev/null and b/static/tiles/7/83/45.png differ diff --git a/static/tiles/7/83/46.png b/static/tiles/7/83/46.png new file mode 100644 index 0000000..84fee61 Binary files /dev/null and b/static/tiles/7/83/46.png differ diff --git a/static/tiles/7/83/47.png b/static/tiles/7/83/47.png new file mode 100644 index 0000000..a042fe2 Binary files /dev/null and b/static/tiles/7/83/47.png differ diff --git a/static/tiles/7/83/48.png b/static/tiles/7/83/48.png new file mode 100644 index 0000000..c35f267 Binary files /dev/null and b/static/tiles/7/83/48.png differ diff --git a/static/tiles/7/83/49.png b/static/tiles/7/83/49.png new file mode 100644 index 0000000..383a006 Binary files /dev/null and b/static/tiles/7/83/49.png differ diff --git a/static/tiles/7/83/50.png b/static/tiles/7/83/50.png new file mode 100644 index 0000000..382b904 Binary files /dev/null and b/static/tiles/7/83/50.png differ diff --git a/static/tiles/7/83/51.png b/static/tiles/7/83/51.png new file mode 100644 index 0000000..2953223 Binary files /dev/null and b/static/tiles/7/83/51.png differ diff --git a/static/tiles/7/83/52.png b/static/tiles/7/83/52.png new file mode 100644 index 0000000..44f1353 Binary files /dev/null and b/static/tiles/7/83/52.png differ diff --git a/static/tiles/7/83/53.png b/static/tiles/7/83/53.png new file mode 100644 index 0000000..f3e3ab7 Binary files /dev/null and b/static/tiles/7/83/53.png differ diff --git a/static/tiles/7/84/37.png b/static/tiles/7/84/37.png new file mode 100644 index 0000000..be5983b Binary files /dev/null and b/static/tiles/7/84/37.png differ diff --git a/static/tiles/7/84/38.png b/static/tiles/7/84/38.png new file mode 100644 index 0000000..9e38c8e Binary files /dev/null and b/static/tiles/7/84/38.png differ diff --git a/static/tiles/7/84/39.png b/static/tiles/7/84/39.png new file mode 100644 index 0000000..a168fe6 Binary files /dev/null and b/static/tiles/7/84/39.png differ diff --git a/static/tiles/7/84/40.png b/static/tiles/7/84/40.png new file mode 100644 index 0000000..4868160 Binary files /dev/null and b/static/tiles/7/84/40.png differ diff --git a/static/tiles/7/84/41.png b/static/tiles/7/84/41.png new file mode 100644 index 0000000..1454ede Binary files /dev/null and b/static/tiles/7/84/41.png differ diff --git a/static/tiles/7/84/42.png b/static/tiles/7/84/42.png new file mode 100644 index 0000000..04a5fb9 Binary files /dev/null and b/static/tiles/7/84/42.png differ diff --git a/static/tiles/7/84/43.png b/static/tiles/7/84/43.png new file mode 100644 index 0000000..2eccd3b Binary files /dev/null and b/static/tiles/7/84/43.png differ diff --git a/static/tiles/7/84/44.png b/static/tiles/7/84/44.png new file mode 100644 index 0000000..142b026 Binary files /dev/null and b/static/tiles/7/84/44.png differ diff --git a/static/tiles/7/84/45.png b/static/tiles/7/84/45.png new file mode 100644 index 0000000..8b33b1a Binary files /dev/null and b/static/tiles/7/84/45.png differ diff --git a/static/tiles/7/84/46.png b/static/tiles/7/84/46.png new file mode 100644 index 0000000..c8e6ee2 Binary files /dev/null and b/static/tiles/7/84/46.png differ diff --git a/static/tiles/7/84/47.png b/static/tiles/7/84/47.png new file mode 100644 index 0000000..2fac305 Binary files /dev/null and b/static/tiles/7/84/47.png differ diff --git a/static/tiles/7/84/48.png b/static/tiles/7/84/48.png new file mode 100644 index 0000000..d60e910 Binary files /dev/null and b/static/tiles/7/84/48.png differ diff --git a/static/tiles/7/84/49.png b/static/tiles/7/84/49.png new file mode 100644 index 0000000..382d7f6 Binary files /dev/null and b/static/tiles/7/84/49.png differ diff --git a/static/tiles/7/84/50.png b/static/tiles/7/84/50.png new file mode 100644 index 0000000..8e3c81a Binary files /dev/null and b/static/tiles/7/84/50.png differ diff --git a/static/tiles/7/84/51.png b/static/tiles/7/84/51.png new file mode 100644 index 0000000..4b487f4 Binary files /dev/null and b/static/tiles/7/84/51.png differ diff --git a/static/tiles/7/84/52.png b/static/tiles/7/84/52.png new file mode 100644 index 0000000..6131825 Binary files /dev/null and b/static/tiles/7/84/52.png differ diff --git a/static/tiles/7/84/53.png b/static/tiles/7/84/53.png new file mode 100644 index 0000000..ef8b8e5 Binary files /dev/null and b/static/tiles/7/84/53.png differ diff --git a/static/tiles/7/85/37.png b/static/tiles/7/85/37.png new file mode 100644 index 0000000..65485b9 Binary files /dev/null and b/static/tiles/7/85/37.png differ diff --git a/static/tiles/7/85/38.png b/static/tiles/7/85/38.png new file mode 100644 index 0000000..682775f Binary files /dev/null and b/static/tiles/7/85/38.png differ diff --git a/static/tiles/7/85/39.png b/static/tiles/7/85/39.png new file mode 100644 index 0000000..f7314c1 Binary files /dev/null and b/static/tiles/7/85/39.png differ diff --git a/static/tiles/7/85/40.png b/static/tiles/7/85/40.png new file mode 100644 index 0000000..6b37b00 Binary files /dev/null and b/static/tiles/7/85/40.png differ diff --git a/static/tiles/7/85/41.png b/static/tiles/7/85/41.png new file mode 100644 index 0000000..7bd160c Binary files /dev/null and b/static/tiles/7/85/41.png differ diff --git a/static/tiles/7/85/42.png b/static/tiles/7/85/42.png new file mode 100644 index 0000000..743958a Binary files /dev/null and b/static/tiles/7/85/42.png differ diff --git a/static/tiles/7/85/43.png b/static/tiles/7/85/43.png new file mode 100644 index 0000000..6d38ff1 Binary files /dev/null and b/static/tiles/7/85/43.png differ diff --git a/static/tiles/7/85/44.png b/static/tiles/7/85/44.png new file mode 100644 index 0000000..da92b36 Binary files /dev/null and b/static/tiles/7/85/44.png differ diff --git a/static/tiles/7/85/45.png b/static/tiles/7/85/45.png new file mode 100644 index 0000000..e6269fc Binary files /dev/null and b/static/tiles/7/85/45.png differ diff --git a/static/tiles/7/85/46.png b/static/tiles/7/85/46.png new file mode 100644 index 0000000..8a0f457 Binary files /dev/null and b/static/tiles/7/85/46.png differ diff --git a/static/tiles/7/85/47.png b/static/tiles/7/85/47.png new file mode 100644 index 0000000..8ba1185 Binary files /dev/null and b/static/tiles/7/85/47.png differ diff --git a/static/tiles/7/85/48.png b/static/tiles/7/85/48.png new file mode 100644 index 0000000..95cb997 Binary files /dev/null and b/static/tiles/7/85/48.png differ diff --git a/static/tiles/7/85/49.png b/static/tiles/7/85/49.png new file mode 100644 index 0000000..39183eb Binary files /dev/null and b/static/tiles/7/85/49.png differ diff --git a/static/tiles/7/85/50.png b/static/tiles/7/85/50.png new file mode 100644 index 0000000..b545a9c Binary files /dev/null and b/static/tiles/7/85/50.png differ diff --git a/static/tiles/7/85/51.png b/static/tiles/7/85/51.png new file mode 100644 index 0000000..7ecdaed Binary files /dev/null and b/static/tiles/7/85/51.png differ diff --git a/static/tiles/7/85/52.png b/static/tiles/7/85/52.png new file mode 100644 index 0000000..285bb9d Binary files /dev/null and b/static/tiles/7/85/52.png differ diff --git a/static/tiles/7/85/53.png b/static/tiles/7/85/53.png new file mode 100644 index 0000000..f2f0e83 Binary files /dev/null and b/static/tiles/7/85/53.png differ diff --git a/static/tiles/7/86/37.png b/static/tiles/7/86/37.png new file mode 100644 index 0000000..5ed1b9a Binary files /dev/null and b/static/tiles/7/86/37.png differ diff --git a/static/tiles/7/86/38.png b/static/tiles/7/86/38.png new file mode 100644 index 0000000..774de06 Binary files /dev/null and b/static/tiles/7/86/38.png differ diff --git a/static/tiles/7/86/39.png b/static/tiles/7/86/39.png new file mode 100644 index 0000000..6a4d729 Binary files /dev/null and b/static/tiles/7/86/39.png differ diff --git a/static/tiles/7/86/40.png b/static/tiles/7/86/40.png new file mode 100644 index 0000000..da8985c Binary files /dev/null and b/static/tiles/7/86/40.png differ diff --git a/static/tiles/7/86/41.png b/static/tiles/7/86/41.png new file mode 100644 index 0000000..f01a56e Binary files /dev/null and b/static/tiles/7/86/41.png differ diff --git a/static/tiles/7/86/42.png b/static/tiles/7/86/42.png new file mode 100644 index 0000000..251a4a2 Binary files /dev/null and b/static/tiles/7/86/42.png differ diff --git a/static/tiles/7/86/43.png b/static/tiles/7/86/43.png new file mode 100644 index 0000000..60ab4cb Binary files /dev/null and b/static/tiles/7/86/43.png differ diff --git a/static/tiles/7/86/44.png b/static/tiles/7/86/44.png new file mode 100644 index 0000000..95c308f Binary files /dev/null and b/static/tiles/7/86/44.png differ diff --git a/static/tiles/7/86/45.png b/static/tiles/7/86/45.png new file mode 100644 index 0000000..ba0b5dd Binary files /dev/null and b/static/tiles/7/86/45.png differ diff --git a/static/tiles/7/86/46.png b/static/tiles/7/86/46.png new file mode 100644 index 0000000..e4400de Binary files /dev/null and b/static/tiles/7/86/46.png differ diff --git a/static/tiles/7/86/47.png b/static/tiles/7/86/47.png new file mode 100644 index 0000000..88c0807 Binary files /dev/null and b/static/tiles/7/86/47.png differ diff --git a/static/tiles/7/86/48.png b/static/tiles/7/86/48.png new file mode 100644 index 0000000..2617798 Binary files /dev/null and b/static/tiles/7/86/48.png differ diff --git a/static/tiles/7/86/49.png b/static/tiles/7/86/49.png new file mode 100644 index 0000000..0982b82 Binary files /dev/null and b/static/tiles/7/86/49.png differ diff --git a/static/tiles/7/86/50.png b/static/tiles/7/86/50.png new file mode 100644 index 0000000..0ffdecf Binary files /dev/null and b/static/tiles/7/86/50.png differ diff --git a/static/tiles/7/86/51.png b/static/tiles/7/86/51.png new file mode 100644 index 0000000..de0ec86 Binary files /dev/null and b/static/tiles/7/86/51.png differ diff --git a/static/tiles/7/86/52.png b/static/tiles/7/86/52.png new file mode 100644 index 0000000..263c6a5 Binary files /dev/null and b/static/tiles/7/86/52.png differ diff --git a/static/tiles/7/86/53.png b/static/tiles/7/86/53.png new file mode 100644 index 0000000..6eff603 Binary files /dev/null and b/static/tiles/7/86/53.png differ diff --git a/static/tiles/8/150/100.png b/static/tiles/8/150/100.png new file mode 100644 index 0000000..71ebb38 Binary files /dev/null and b/static/tiles/8/150/100.png differ diff --git a/static/tiles/8/150/101.png b/static/tiles/8/150/101.png new file mode 100644 index 0000000..b10d01b Binary files /dev/null and b/static/tiles/8/150/101.png differ diff --git a/static/tiles/8/150/102.png b/static/tiles/8/150/102.png new file mode 100644 index 0000000..f607ae0 Binary files /dev/null and b/static/tiles/8/150/102.png differ diff --git a/static/tiles/8/150/103.png b/static/tiles/8/150/103.png new file mode 100644 index 0000000..79e6947 Binary files /dev/null and b/static/tiles/8/150/103.png differ diff --git a/static/tiles/8/150/104.png b/static/tiles/8/150/104.png new file mode 100644 index 0000000..6959e4b Binary files /dev/null and b/static/tiles/8/150/104.png differ diff --git a/static/tiles/8/150/105.png b/static/tiles/8/150/105.png new file mode 100644 index 0000000..4436264 Binary files /dev/null and b/static/tiles/8/150/105.png differ diff --git a/static/tiles/8/150/106.png b/static/tiles/8/150/106.png new file mode 100644 index 0000000..b27b1dd Binary files /dev/null and b/static/tiles/8/150/106.png differ diff --git a/static/tiles/8/150/75.png b/static/tiles/8/150/75.png new file mode 100644 index 0000000..710a8f4 Binary files /dev/null and b/static/tiles/8/150/75.png differ diff --git a/static/tiles/8/150/76.png b/static/tiles/8/150/76.png new file mode 100644 index 0000000..574c033 Binary files /dev/null and b/static/tiles/8/150/76.png differ diff --git a/static/tiles/8/150/77.png b/static/tiles/8/150/77.png new file mode 100644 index 0000000..d205e35 Binary files /dev/null and b/static/tiles/8/150/77.png differ diff --git a/static/tiles/8/150/78.png b/static/tiles/8/150/78.png new file mode 100644 index 0000000..e7fc4c5 Binary files /dev/null and b/static/tiles/8/150/78.png differ diff --git a/static/tiles/8/150/79.png b/static/tiles/8/150/79.png new file mode 100644 index 0000000..7fc7686 Binary files /dev/null and b/static/tiles/8/150/79.png differ diff --git a/static/tiles/8/150/80.png b/static/tiles/8/150/80.png new file mode 100644 index 0000000..96ff485 Binary files /dev/null and b/static/tiles/8/150/80.png differ diff --git a/static/tiles/8/150/81.png b/static/tiles/8/150/81.png new file mode 100644 index 0000000..5ad0afc Binary files /dev/null and b/static/tiles/8/150/81.png differ diff --git a/static/tiles/8/150/82.png b/static/tiles/8/150/82.png new file mode 100644 index 0000000..5a49c72 Binary files /dev/null and b/static/tiles/8/150/82.png differ diff --git a/static/tiles/8/150/83.png b/static/tiles/8/150/83.png new file mode 100644 index 0000000..4664cb5 Binary files /dev/null and b/static/tiles/8/150/83.png differ diff --git a/static/tiles/8/150/84.png b/static/tiles/8/150/84.png new file mode 100644 index 0000000..cc4f224 Binary files /dev/null and b/static/tiles/8/150/84.png differ diff --git a/static/tiles/8/150/85.png b/static/tiles/8/150/85.png new file mode 100644 index 0000000..c604ef5 Binary files /dev/null and b/static/tiles/8/150/85.png differ diff --git a/static/tiles/8/150/86.png b/static/tiles/8/150/86.png new file mode 100644 index 0000000..63e1a7f Binary files /dev/null and b/static/tiles/8/150/86.png differ diff --git a/static/tiles/8/150/87.png b/static/tiles/8/150/87.png new file mode 100644 index 0000000..6650b57 Binary files /dev/null and b/static/tiles/8/150/87.png differ diff --git a/static/tiles/8/150/88.png b/static/tiles/8/150/88.png new file mode 100644 index 0000000..3d55237 Binary files /dev/null and b/static/tiles/8/150/88.png differ diff --git a/static/tiles/8/150/89.png b/static/tiles/8/150/89.png new file mode 100644 index 0000000..69f0687 Binary files /dev/null and b/static/tiles/8/150/89.png differ diff --git a/static/tiles/8/150/90.png b/static/tiles/8/150/90.png new file mode 100644 index 0000000..15f8c5e Binary files /dev/null and b/static/tiles/8/150/90.png differ diff --git a/static/tiles/8/150/91.png b/static/tiles/8/150/91.png new file mode 100644 index 0000000..31a58ea Binary files /dev/null and b/static/tiles/8/150/91.png differ diff --git a/static/tiles/8/150/92.png b/static/tiles/8/150/92.png new file mode 100644 index 0000000..341cf65 Binary files /dev/null and b/static/tiles/8/150/92.png differ diff --git a/static/tiles/8/150/93.png b/static/tiles/8/150/93.png new file mode 100644 index 0000000..b08d79d Binary files /dev/null and b/static/tiles/8/150/93.png differ diff --git a/static/tiles/8/150/94.png b/static/tiles/8/150/94.png new file mode 100644 index 0000000..05ca483 Binary files /dev/null and b/static/tiles/8/150/94.png differ diff --git a/static/tiles/8/150/95.png b/static/tiles/8/150/95.png new file mode 100644 index 0000000..407a3d7 Binary files /dev/null and b/static/tiles/8/150/95.png differ diff --git a/static/tiles/8/150/96.png b/static/tiles/8/150/96.png new file mode 100644 index 0000000..be245d8 Binary files /dev/null and b/static/tiles/8/150/96.png differ diff --git a/static/tiles/8/150/97.png b/static/tiles/8/150/97.png new file mode 100644 index 0000000..9c0c0c8 Binary files /dev/null and b/static/tiles/8/150/97.png differ diff --git a/static/tiles/8/150/98.png b/static/tiles/8/150/98.png new file mode 100644 index 0000000..40d5a2e Binary files /dev/null and b/static/tiles/8/150/98.png differ diff --git a/static/tiles/8/150/99.png b/static/tiles/8/150/99.png new file mode 100644 index 0000000..26ee148 Binary files /dev/null and b/static/tiles/8/150/99.png differ diff --git a/static/tiles/8/151/100.png b/static/tiles/8/151/100.png new file mode 100644 index 0000000..511a189 Binary files /dev/null and b/static/tiles/8/151/100.png differ diff --git a/static/tiles/8/151/101.png b/static/tiles/8/151/101.png new file mode 100644 index 0000000..f0a833d Binary files /dev/null and b/static/tiles/8/151/101.png differ diff --git a/static/tiles/8/151/102.png b/static/tiles/8/151/102.png new file mode 100644 index 0000000..9ae719c Binary files /dev/null and b/static/tiles/8/151/102.png differ diff --git a/static/tiles/8/151/103.png b/static/tiles/8/151/103.png new file mode 100644 index 0000000..2c487c4 Binary files /dev/null and b/static/tiles/8/151/103.png differ diff --git a/static/tiles/8/151/104.png b/static/tiles/8/151/104.png new file mode 100644 index 0000000..2d4eac4 Binary files /dev/null and b/static/tiles/8/151/104.png differ diff --git a/static/tiles/8/151/105.png b/static/tiles/8/151/105.png new file mode 100644 index 0000000..9265fa3 Binary files /dev/null and b/static/tiles/8/151/105.png differ diff --git a/static/tiles/8/151/106.png b/static/tiles/8/151/106.png new file mode 100644 index 0000000..995c6b5 Binary files /dev/null and b/static/tiles/8/151/106.png differ diff --git a/static/tiles/8/151/75.png b/static/tiles/8/151/75.png new file mode 100644 index 0000000..c939ed6 Binary files /dev/null and b/static/tiles/8/151/75.png differ diff --git a/static/tiles/8/151/76.png b/static/tiles/8/151/76.png new file mode 100644 index 0000000..9f4b97b Binary files /dev/null and b/static/tiles/8/151/76.png differ diff --git a/static/tiles/8/151/77.png b/static/tiles/8/151/77.png new file mode 100644 index 0000000..322723b Binary files /dev/null and b/static/tiles/8/151/77.png differ diff --git a/static/tiles/8/151/78.png b/static/tiles/8/151/78.png new file mode 100644 index 0000000..3382b35 Binary files /dev/null and b/static/tiles/8/151/78.png differ diff --git a/static/tiles/8/151/79.png b/static/tiles/8/151/79.png new file mode 100644 index 0000000..da0d974 Binary files /dev/null and b/static/tiles/8/151/79.png differ diff --git a/static/tiles/8/151/80.png b/static/tiles/8/151/80.png new file mode 100644 index 0000000..437c55c Binary files /dev/null and b/static/tiles/8/151/80.png differ diff --git a/static/tiles/8/151/81.png b/static/tiles/8/151/81.png new file mode 100644 index 0000000..f418a9d Binary files /dev/null and b/static/tiles/8/151/81.png differ diff --git a/static/tiles/8/151/82.png b/static/tiles/8/151/82.png new file mode 100644 index 0000000..989dbca Binary files /dev/null and b/static/tiles/8/151/82.png differ diff --git a/static/tiles/8/151/83.png b/static/tiles/8/151/83.png new file mode 100644 index 0000000..70973c1 Binary files /dev/null and b/static/tiles/8/151/83.png differ diff --git a/static/tiles/8/151/84.png b/static/tiles/8/151/84.png new file mode 100644 index 0000000..7be31f9 Binary files /dev/null and b/static/tiles/8/151/84.png differ diff --git a/static/tiles/8/151/85.png b/static/tiles/8/151/85.png new file mode 100644 index 0000000..6ad43a6 Binary files /dev/null and b/static/tiles/8/151/85.png differ diff --git a/static/tiles/8/151/86.png b/static/tiles/8/151/86.png new file mode 100644 index 0000000..548c814 Binary files /dev/null and b/static/tiles/8/151/86.png differ diff --git a/static/tiles/8/151/87.png b/static/tiles/8/151/87.png new file mode 100644 index 0000000..c2a4b37 Binary files /dev/null and b/static/tiles/8/151/87.png differ diff --git a/static/tiles/8/151/88.png b/static/tiles/8/151/88.png new file mode 100644 index 0000000..30e0fcb Binary files /dev/null and b/static/tiles/8/151/88.png differ diff --git a/static/tiles/8/151/89.png b/static/tiles/8/151/89.png new file mode 100644 index 0000000..76711d2 Binary files /dev/null and b/static/tiles/8/151/89.png differ diff --git a/static/tiles/8/151/90.png b/static/tiles/8/151/90.png new file mode 100644 index 0000000..1d0d78f Binary files /dev/null and b/static/tiles/8/151/90.png differ diff --git a/static/tiles/8/151/91.png b/static/tiles/8/151/91.png new file mode 100644 index 0000000..4d80800 Binary files /dev/null and b/static/tiles/8/151/91.png differ diff --git a/static/tiles/8/151/92.png b/static/tiles/8/151/92.png new file mode 100644 index 0000000..c30d8b4 Binary files /dev/null and b/static/tiles/8/151/92.png differ diff --git a/static/tiles/8/151/93.png b/static/tiles/8/151/93.png new file mode 100644 index 0000000..a064a4f Binary files /dev/null and b/static/tiles/8/151/93.png differ diff --git a/static/tiles/8/151/94.png b/static/tiles/8/151/94.png new file mode 100644 index 0000000..ef50aff Binary files /dev/null and b/static/tiles/8/151/94.png differ diff --git a/static/tiles/8/151/95.png b/static/tiles/8/151/95.png new file mode 100644 index 0000000..79475cd Binary files /dev/null and b/static/tiles/8/151/95.png differ diff --git a/static/tiles/8/151/96.png b/static/tiles/8/151/96.png new file mode 100644 index 0000000..274d2d2 Binary files /dev/null and b/static/tiles/8/151/96.png differ diff --git a/static/tiles/8/151/97.png b/static/tiles/8/151/97.png new file mode 100644 index 0000000..1868a2c Binary files /dev/null and b/static/tiles/8/151/97.png differ diff --git a/static/tiles/8/151/98.png b/static/tiles/8/151/98.png new file mode 100644 index 0000000..eb80abd Binary files /dev/null and b/static/tiles/8/151/98.png differ diff --git a/static/tiles/8/151/99.png b/static/tiles/8/151/99.png new file mode 100644 index 0000000..a3a77f2 Binary files /dev/null and b/static/tiles/8/151/99.png differ diff --git a/static/tiles/8/152/100.png b/static/tiles/8/152/100.png new file mode 100644 index 0000000..e0e01f4 Binary files /dev/null and b/static/tiles/8/152/100.png differ diff --git a/static/tiles/8/152/101.png b/static/tiles/8/152/101.png new file mode 100644 index 0000000..953192d Binary files /dev/null and b/static/tiles/8/152/101.png differ diff --git a/static/tiles/8/152/102.png b/static/tiles/8/152/102.png new file mode 100644 index 0000000..1572540 Binary files /dev/null and b/static/tiles/8/152/102.png differ diff --git a/static/tiles/8/152/103.png b/static/tiles/8/152/103.png new file mode 100644 index 0000000..32d5895 Binary files /dev/null and b/static/tiles/8/152/103.png differ diff --git a/static/tiles/8/152/104.png b/static/tiles/8/152/104.png new file mode 100644 index 0000000..7e4eaa5 Binary files /dev/null and b/static/tiles/8/152/104.png differ diff --git a/static/tiles/8/152/105.png b/static/tiles/8/152/105.png new file mode 100644 index 0000000..27f4c9a Binary files /dev/null and b/static/tiles/8/152/105.png differ diff --git a/static/tiles/8/152/106.png b/static/tiles/8/152/106.png new file mode 100644 index 0000000..f874880 Binary files /dev/null and b/static/tiles/8/152/106.png differ diff --git a/static/tiles/8/152/75.png b/static/tiles/8/152/75.png new file mode 100644 index 0000000..f4cb06f Binary files /dev/null and b/static/tiles/8/152/75.png differ diff --git a/static/tiles/8/152/76.png b/static/tiles/8/152/76.png new file mode 100644 index 0000000..baaf156 Binary files /dev/null and b/static/tiles/8/152/76.png differ diff --git a/static/tiles/8/152/77.png b/static/tiles/8/152/77.png new file mode 100644 index 0000000..8f45ec3 Binary files /dev/null and b/static/tiles/8/152/77.png differ diff --git a/static/tiles/8/152/78.png b/static/tiles/8/152/78.png new file mode 100644 index 0000000..74c6680 Binary files /dev/null and b/static/tiles/8/152/78.png differ diff --git a/static/tiles/8/152/79.png b/static/tiles/8/152/79.png new file mode 100644 index 0000000..3fd8322 Binary files /dev/null and b/static/tiles/8/152/79.png differ diff --git a/static/tiles/8/152/80.png b/static/tiles/8/152/80.png new file mode 100644 index 0000000..2b745ac Binary files /dev/null and b/static/tiles/8/152/80.png differ diff --git a/static/tiles/8/152/81.png b/static/tiles/8/152/81.png new file mode 100644 index 0000000..300358a Binary files /dev/null and b/static/tiles/8/152/81.png differ diff --git a/static/tiles/8/152/82.png b/static/tiles/8/152/82.png new file mode 100644 index 0000000..5e820af Binary files /dev/null and b/static/tiles/8/152/82.png differ diff --git a/static/tiles/8/152/83.png b/static/tiles/8/152/83.png new file mode 100644 index 0000000..e2aec1a Binary files /dev/null and b/static/tiles/8/152/83.png differ diff --git a/static/tiles/8/152/84.png b/static/tiles/8/152/84.png new file mode 100644 index 0000000..5ff35cd Binary files /dev/null and b/static/tiles/8/152/84.png differ diff --git a/static/tiles/8/152/85.png b/static/tiles/8/152/85.png new file mode 100644 index 0000000..e88ae50 Binary files /dev/null and b/static/tiles/8/152/85.png differ diff --git a/static/tiles/8/152/86.png b/static/tiles/8/152/86.png new file mode 100644 index 0000000..4197a5b Binary files /dev/null and b/static/tiles/8/152/86.png differ diff --git a/static/tiles/8/152/87.png b/static/tiles/8/152/87.png new file mode 100644 index 0000000..2e680ee Binary files /dev/null and b/static/tiles/8/152/87.png differ diff --git a/static/tiles/8/152/88.png b/static/tiles/8/152/88.png new file mode 100644 index 0000000..e2372a5 Binary files /dev/null and b/static/tiles/8/152/88.png differ diff --git a/static/tiles/8/152/89.png b/static/tiles/8/152/89.png new file mode 100644 index 0000000..e2221d8 Binary files /dev/null and b/static/tiles/8/152/89.png differ diff --git a/static/tiles/8/152/90.png b/static/tiles/8/152/90.png new file mode 100644 index 0000000..b06aa66 Binary files /dev/null and b/static/tiles/8/152/90.png differ diff --git a/static/tiles/8/152/91.png b/static/tiles/8/152/91.png new file mode 100644 index 0000000..b9d8322 Binary files /dev/null and b/static/tiles/8/152/91.png differ diff --git a/static/tiles/8/152/92.png b/static/tiles/8/152/92.png new file mode 100644 index 0000000..a9d060f Binary files /dev/null and b/static/tiles/8/152/92.png differ diff --git a/static/tiles/8/152/93.png b/static/tiles/8/152/93.png new file mode 100644 index 0000000..bac8261 Binary files /dev/null and b/static/tiles/8/152/93.png differ diff --git a/static/tiles/8/152/94.png b/static/tiles/8/152/94.png new file mode 100644 index 0000000..5f80c4a Binary files /dev/null and b/static/tiles/8/152/94.png differ diff --git a/static/tiles/8/152/95.png b/static/tiles/8/152/95.png new file mode 100644 index 0000000..382ae99 Binary files /dev/null and b/static/tiles/8/152/95.png differ diff --git a/static/tiles/8/152/96.png b/static/tiles/8/152/96.png new file mode 100644 index 0000000..c468585 Binary files /dev/null and b/static/tiles/8/152/96.png differ diff --git a/static/tiles/8/152/97.png b/static/tiles/8/152/97.png new file mode 100644 index 0000000..3773223 Binary files /dev/null and b/static/tiles/8/152/97.png differ diff --git a/static/tiles/8/152/98.png b/static/tiles/8/152/98.png new file mode 100644 index 0000000..67eb3ba Binary files /dev/null and b/static/tiles/8/152/98.png differ diff --git a/static/tiles/8/152/99.png b/static/tiles/8/152/99.png new file mode 100644 index 0000000..4663262 Binary files /dev/null and b/static/tiles/8/152/99.png differ diff --git a/static/tiles/8/153/100.png b/static/tiles/8/153/100.png new file mode 100644 index 0000000..f5a4521 Binary files /dev/null and b/static/tiles/8/153/100.png differ diff --git a/static/tiles/8/153/101.png b/static/tiles/8/153/101.png new file mode 100644 index 0000000..9857bc1 Binary files /dev/null and b/static/tiles/8/153/101.png differ diff --git a/static/tiles/8/153/102.png b/static/tiles/8/153/102.png new file mode 100644 index 0000000..c6f9fbd Binary files /dev/null and b/static/tiles/8/153/102.png differ diff --git a/static/tiles/8/153/103.png b/static/tiles/8/153/103.png new file mode 100644 index 0000000..2e5f500 Binary files /dev/null and b/static/tiles/8/153/103.png differ diff --git a/static/tiles/8/153/104.png b/static/tiles/8/153/104.png new file mode 100644 index 0000000..c6406bd Binary files /dev/null and b/static/tiles/8/153/104.png differ diff --git a/static/tiles/8/153/105.png b/static/tiles/8/153/105.png new file mode 100644 index 0000000..f85fe7f Binary files /dev/null and b/static/tiles/8/153/105.png differ diff --git a/static/tiles/8/153/106.png b/static/tiles/8/153/106.png new file mode 100644 index 0000000..326387b Binary files /dev/null and b/static/tiles/8/153/106.png differ diff --git a/static/tiles/8/153/75.png b/static/tiles/8/153/75.png new file mode 100644 index 0000000..7d101fd Binary files /dev/null and b/static/tiles/8/153/75.png differ diff --git a/static/tiles/8/153/76.png b/static/tiles/8/153/76.png new file mode 100644 index 0000000..8122057 Binary files /dev/null and b/static/tiles/8/153/76.png differ diff --git a/static/tiles/8/153/77.png b/static/tiles/8/153/77.png new file mode 100644 index 0000000..98795da Binary files /dev/null and b/static/tiles/8/153/77.png differ diff --git a/static/tiles/8/153/78.png b/static/tiles/8/153/78.png new file mode 100644 index 0000000..c396a2a Binary files /dev/null and b/static/tiles/8/153/78.png differ diff --git a/static/tiles/8/153/79.png b/static/tiles/8/153/79.png new file mode 100644 index 0000000..8d8d710 Binary files /dev/null and b/static/tiles/8/153/79.png differ diff --git a/static/tiles/8/153/80.png b/static/tiles/8/153/80.png new file mode 100644 index 0000000..0ff23d3 Binary files /dev/null and b/static/tiles/8/153/80.png differ diff --git a/static/tiles/8/153/81.png b/static/tiles/8/153/81.png new file mode 100644 index 0000000..5b1735e Binary files /dev/null and b/static/tiles/8/153/81.png differ diff --git a/static/tiles/8/153/82.png b/static/tiles/8/153/82.png new file mode 100644 index 0000000..f5a9a54 Binary files /dev/null and b/static/tiles/8/153/82.png differ diff --git a/static/tiles/8/153/83.png b/static/tiles/8/153/83.png new file mode 100644 index 0000000..8796078 Binary files /dev/null and b/static/tiles/8/153/83.png differ diff --git a/static/tiles/8/153/84.png b/static/tiles/8/153/84.png new file mode 100644 index 0000000..9229b8a Binary files /dev/null and b/static/tiles/8/153/84.png differ diff --git a/static/tiles/8/153/85.png b/static/tiles/8/153/85.png new file mode 100644 index 0000000..cc8ddd8 Binary files /dev/null and b/static/tiles/8/153/85.png differ diff --git a/static/tiles/8/153/86.png b/static/tiles/8/153/86.png new file mode 100644 index 0000000..874865e Binary files /dev/null and b/static/tiles/8/153/86.png differ diff --git a/static/tiles/8/153/87.png b/static/tiles/8/153/87.png new file mode 100644 index 0000000..b2341d5 Binary files /dev/null and b/static/tiles/8/153/87.png differ diff --git a/static/tiles/8/153/88.png b/static/tiles/8/153/88.png new file mode 100644 index 0000000..3e107a6 Binary files /dev/null and b/static/tiles/8/153/88.png differ diff --git a/static/tiles/8/153/89.png b/static/tiles/8/153/89.png new file mode 100644 index 0000000..e1ff7e5 Binary files /dev/null and b/static/tiles/8/153/89.png differ diff --git a/static/tiles/8/153/90.png b/static/tiles/8/153/90.png new file mode 100644 index 0000000..694c795 Binary files /dev/null and b/static/tiles/8/153/90.png differ diff --git a/static/tiles/8/153/91.png b/static/tiles/8/153/91.png new file mode 100644 index 0000000..cb8b37d Binary files /dev/null and b/static/tiles/8/153/91.png differ diff --git a/static/tiles/8/153/92.png b/static/tiles/8/153/92.png new file mode 100644 index 0000000..ce059e4 Binary files /dev/null and b/static/tiles/8/153/92.png differ diff --git a/static/tiles/8/153/93.png b/static/tiles/8/153/93.png new file mode 100644 index 0000000..787a43c Binary files /dev/null and b/static/tiles/8/153/93.png differ diff --git a/static/tiles/8/153/94.png b/static/tiles/8/153/94.png new file mode 100644 index 0000000..ebcad41 Binary files /dev/null and b/static/tiles/8/153/94.png differ diff --git a/static/tiles/8/153/95.png b/static/tiles/8/153/95.png new file mode 100644 index 0000000..5bea793 Binary files /dev/null and b/static/tiles/8/153/95.png differ diff --git a/static/tiles/8/153/96.png b/static/tiles/8/153/96.png new file mode 100644 index 0000000..dbb2c75 Binary files /dev/null and b/static/tiles/8/153/96.png differ diff --git a/static/tiles/8/153/97.png b/static/tiles/8/153/97.png new file mode 100644 index 0000000..8b88574 Binary files /dev/null and b/static/tiles/8/153/97.png differ diff --git a/static/tiles/8/153/98.png b/static/tiles/8/153/98.png new file mode 100644 index 0000000..1f93ec0 Binary files /dev/null and b/static/tiles/8/153/98.png differ diff --git a/static/tiles/8/153/99.png b/static/tiles/8/153/99.png new file mode 100644 index 0000000..0176da9 Binary files /dev/null and b/static/tiles/8/153/99.png differ diff --git a/static/tiles/8/154/100.png b/static/tiles/8/154/100.png new file mode 100644 index 0000000..5e26b56 Binary files /dev/null and b/static/tiles/8/154/100.png differ diff --git a/static/tiles/8/154/101.png b/static/tiles/8/154/101.png new file mode 100644 index 0000000..8cc45da Binary files /dev/null and b/static/tiles/8/154/101.png differ diff --git a/static/tiles/8/154/102.png b/static/tiles/8/154/102.png new file mode 100644 index 0000000..3c303d7 Binary files /dev/null and b/static/tiles/8/154/102.png differ diff --git a/static/tiles/8/154/103.png b/static/tiles/8/154/103.png new file mode 100644 index 0000000..bc7880c Binary files /dev/null and b/static/tiles/8/154/103.png differ diff --git a/static/tiles/8/154/104.png b/static/tiles/8/154/104.png new file mode 100644 index 0000000..55acefe Binary files /dev/null and b/static/tiles/8/154/104.png differ diff --git a/static/tiles/8/154/105.png b/static/tiles/8/154/105.png new file mode 100644 index 0000000..2c0fa82 Binary files /dev/null and b/static/tiles/8/154/105.png differ diff --git a/static/tiles/8/154/106.png b/static/tiles/8/154/106.png new file mode 100644 index 0000000..0d13a19 Binary files /dev/null and b/static/tiles/8/154/106.png differ diff --git a/static/tiles/8/154/75.png b/static/tiles/8/154/75.png new file mode 100644 index 0000000..9848bd7 Binary files /dev/null and b/static/tiles/8/154/75.png differ diff --git a/static/tiles/8/154/76.png b/static/tiles/8/154/76.png new file mode 100644 index 0000000..00087bb Binary files /dev/null and b/static/tiles/8/154/76.png differ diff --git a/static/tiles/8/154/77.png b/static/tiles/8/154/77.png new file mode 100644 index 0000000..2b35c6f Binary files /dev/null and b/static/tiles/8/154/77.png differ diff --git a/static/tiles/8/154/78.png b/static/tiles/8/154/78.png new file mode 100644 index 0000000..d02efdc Binary files /dev/null and b/static/tiles/8/154/78.png differ diff --git a/static/tiles/8/154/79.png b/static/tiles/8/154/79.png new file mode 100644 index 0000000..b7c52fa Binary files /dev/null and b/static/tiles/8/154/79.png differ diff --git a/static/tiles/8/154/80.png b/static/tiles/8/154/80.png new file mode 100644 index 0000000..b55e336 Binary files /dev/null and b/static/tiles/8/154/80.png differ diff --git a/static/tiles/8/154/81.png b/static/tiles/8/154/81.png new file mode 100644 index 0000000..67041ef Binary files /dev/null and b/static/tiles/8/154/81.png differ diff --git a/static/tiles/8/154/82.png b/static/tiles/8/154/82.png new file mode 100644 index 0000000..cd796d6 Binary files /dev/null and b/static/tiles/8/154/82.png differ diff --git a/static/tiles/8/154/83.png b/static/tiles/8/154/83.png new file mode 100644 index 0000000..55ab283 Binary files /dev/null and b/static/tiles/8/154/83.png differ diff --git a/static/tiles/8/154/84.png b/static/tiles/8/154/84.png new file mode 100644 index 0000000..45434b1 Binary files /dev/null and b/static/tiles/8/154/84.png differ diff --git a/static/tiles/8/154/85.png b/static/tiles/8/154/85.png new file mode 100644 index 0000000..3151ac8 Binary files /dev/null and b/static/tiles/8/154/85.png differ diff --git a/static/tiles/8/154/86.png b/static/tiles/8/154/86.png new file mode 100644 index 0000000..f326d47 Binary files /dev/null and b/static/tiles/8/154/86.png differ diff --git a/static/tiles/8/154/87.png b/static/tiles/8/154/87.png new file mode 100644 index 0000000..ee43096 Binary files /dev/null and b/static/tiles/8/154/87.png differ diff --git a/static/tiles/8/154/88.png b/static/tiles/8/154/88.png new file mode 100644 index 0000000..6b5110d Binary files /dev/null and b/static/tiles/8/154/88.png differ diff --git a/static/tiles/8/154/89.png b/static/tiles/8/154/89.png new file mode 100644 index 0000000..6ce56c1 Binary files /dev/null and b/static/tiles/8/154/89.png differ diff --git a/static/tiles/8/154/90.png b/static/tiles/8/154/90.png new file mode 100644 index 0000000..aa578bb Binary files /dev/null and b/static/tiles/8/154/90.png differ diff --git a/static/tiles/8/154/91.png b/static/tiles/8/154/91.png new file mode 100644 index 0000000..59e1928 Binary files /dev/null and b/static/tiles/8/154/91.png differ diff --git a/static/tiles/8/154/92.png b/static/tiles/8/154/92.png new file mode 100644 index 0000000..908030e Binary files /dev/null and b/static/tiles/8/154/92.png differ diff --git a/static/tiles/8/154/93.png b/static/tiles/8/154/93.png new file mode 100644 index 0000000..94b6390 Binary files /dev/null and b/static/tiles/8/154/93.png differ diff --git a/static/tiles/8/154/94.png b/static/tiles/8/154/94.png new file mode 100644 index 0000000..8b86c9d Binary files /dev/null and b/static/tiles/8/154/94.png differ diff --git a/static/tiles/8/154/95.png b/static/tiles/8/154/95.png new file mode 100644 index 0000000..6b3c383 Binary files /dev/null and b/static/tiles/8/154/95.png differ diff --git a/static/tiles/8/154/96.png b/static/tiles/8/154/96.png new file mode 100644 index 0000000..c99b06e Binary files /dev/null and b/static/tiles/8/154/96.png differ diff --git a/static/tiles/8/154/97.png b/static/tiles/8/154/97.png new file mode 100644 index 0000000..815f0bb Binary files /dev/null and b/static/tiles/8/154/97.png differ diff --git a/static/tiles/8/154/98.png b/static/tiles/8/154/98.png new file mode 100644 index 0000000..435faa0 Binary files /dev/null and b/static/tiles/8/154/98.png differ diff --git a/static/tiles/8/154/99.png b/static/tiles/8/154/99.png new file mode 100644 index 0000000..48b28f1 Binary files /dev/null and b/static/tiles/8/154/99.png differ diff --git a/static/tiles/8/155/100.png b/static/tiles/8/155/100.png new file mode 100644 index 0000000..7ebe313 Binary files /dev/null and b/static/tiles/8/155/100.png differ diff --git a/static/tiles/8/155/101.png b/static/tiles/8/155/101.png new file mode 100644 index 0000000..879238e Binary files /dev/null and b/static/tiles/8/155/101.png differ diff --git a/static/tiles/8/155/102.png b/static/tiles/8/155/102.png new file mode 100644 index 0000000..96afc25 Binary files /dev/null and b/static/tiles/8/155/102.png differ diff --git a/static/tiles/8/155/103.png b/static/tiles/8/155/103.png new file mode 100644 index 0000000..431b0b3 Binary files /dev/null and b/static/tiles/8/155/103.png differ diff --git a/static/tiles/8/155/104.png b/static/tiles/8/155/104.png new file mode 100644 index 0000000..6d04241 Binary files /dev/null and b/static/tiles/8/155/104.png differ diff --git a/static/tiles/8/155/105.png b/static/tiles/8/155/105.png new file mode 100644 index 0000000..bca0ab5 Binary files /dev/null and b/static/tiles/8/155/105.png differ diff --git a/static/tiles/8/155/106.png b/static/tiles/8/155/106.png new file mode 100644 index 0000000..8ac3c4b Binary files /dev/null and b/static/tiles/8/155/106.png differ diff --git a/static/tiles/8/155/75.png b/static/tiles/8/155/75.png new file mode 100644 index 0000000..f5594ad Binary files /dev/null and b/static/tiles/8/155/75.png differ diff --git a/static/tiles/8/155/76.png b/static/tiles/8/155/76.png new file mode 100644 index 0000000..53597e6 Binary files /dev/null and b/static/tiles/8/155/76.png differ diff --git a/static/tiles/8/155/77.png b/static/tiles/8/155/77.png new file mode 100644 index 0000000..755b064 Binary files /dev/null and b/static/tiles/8/155/77.png differ diff --git a/static/tiles/8/155/78.png b/static/tiles/8/155/78.png new file mode 100644 index 0000000..9b790a5 Binary files /dev/null and b/static/tiles/8/155/78.png differ diff --git a/static/tiles/8/155/79.png b/static/tiles/8/155/79.png new file mode 100644 index 0000000..40b4108 Binary files /dev/null and b/static/tiles/8/155/79.png differ diff --git a/static/tiles/8/155/80.png b/static/tiles/8/155/80.png new file mode 100644 index 0000000..7b95dd8 Binary files /dev/null and b/static/tiles/8/155/80.png differ diff --git a/static/tiles/8/155/81.png b/static/tiles/8/155/81.png new file mode 100644 index 0000000..771b9d4 Binary files /dev/null and b/static/tiles/8/155/81.png differ diff --git a/static/tiles/8/155/82.png b/static/tiles/8/155/82.png new file mode 100644 index 0000000..a6d1cec Binary files /dev/null and b/static/tiles/8/155/82.png differ diff --git a/static/tiles/8/155/83.png b/static/tiles/8/155/83.png new file mode 100644 index 0000000..0b7467f Binary files /dev/null and b/static/tiles/8/155/83.png differ diff --git a/static/tiles/8/155/84.png b/static/tiles/8/155/84.png new file mode 100644 index 0000000..644efdc Binary files /dev/null and b/static/tiles/8/155/84.png differ diff --git a/static/tiles/8/155/85.png b/static/tiles/8/155/85.png new file mode 100644 index 0000000..468fea6 Binary files /dev/null and b/static/tiles/8/155/85.png differ diff --git a/static/tiles/8/155/86.png b/static/tiles/8/155/86.png new file mode 100644 index 0000000..028714c Binary files /dev/null and b/static/tiles/8/155/86.png differ diff --git a/static/tiles/8/155/87.png b/static/tiles/8/155/87.png new file mode 100644 index 0000000..3d4a54d Binary files /dev/null and b/static/tiles/8/155/87.png differ diff --git a/static/tiles/8/155/88.png b/static/tiles/8/155/88.png new file mode 100644 index 0000000..4cf98c2 Binary files /dev/null and b/static/tiles/8/155/88.png differ diff --git a/static/tiles/8/155/89.png b/static/tiles/8/155/89.png new file mode 100644 index 0000000..478bc9d Binary files /dev/null and b/static/tiles/8/155/89.png differ diff --git a/static/tiles/8/155/90.png b/static/tiles/8/155/90.png new file mode 100644 index 0000000..977583a Binary files /dev/null and b/static/tiles/8/155/90.png differ diff --git a/static/tiles/8/155/91.png b/static/tiles/8/155/91.png new file mode 100644 index 0000000..dafa88b Binary files /dev/null and b/static/tiles/8/155/91.png differ diff --git a/static/tiles/8/155/92.png b/static/tiles/8/155/92.png new file mode 100644 index 0000000..cd72bad Binary files /dev/null and b/static/tiles/8/155/92.png differ diff --git a/static/tiles/8/155/93.png b/static/tiles/8/155/93.png new file mode 100644 index 0000000..e6841d5 Binary files /dev/null and b/static/tiles/8/155/93.png differ diff --git a/static/tiles/8/155/94.png b/static/tiles/8/155/94.png new file mode 100644 index 0000000..b818f5a Binary files /dev/null and b/static/tiles/8/155/94.png differ diff --git a/static/tiles/8/155/95.png b/static/tiles/8/155/95.png new file mode 100644 index 0000000..46aca9b Binary files /dev/null and b/static/tiles/8/155/95.png differ diff --git a/static/tiles/8/155/96.png b/static/tiles/8/155/96.png new file mode 100644 index 0000000..b747f1a Binary files /dev/null and b/static/tiles/8/155/96.png differ diff --git a/static/tiles/8/155/97.png b/static/tiles/8/155/97.png new file mode 100644 index 0000000..99167ed Binary files /dev/null and b/static/tiles/8/155/97.png differ diff --git a/static/tiles/8/155/98.png b/static/tiles/8/155/98.png new file mode 100644 index 0000000..ca97bb2 Binary files /dev/null and b/static/tiles/8/155/98.png differ diff --git a/static/tiles/8/155/99.png b/static/tiles/8/155/99.png new file mode 100644 index 0000000..970a0cd Binary files /dev/null and b/static/tiles/8/155/99.png differ diff --git a/static/tiles/8/156/100.png b/static/tiles/8/156/100.png new file mode 100644 index 0000000..e2ed3b9 Binary files /dev/null and b/static/tiles/8/156/100.png differ diff --git a/static/tiles/8/156/101.png b/static/tiles/8/156/101.png new file mode 100644 index 0000000..aa6614c Binary files /dev/null and b/static/tiles/8/156/101.png differ diff --git a/static/tiles/8/156/102.png b/static/tiles/8/156/102.png new file mode 100644 index 0000000..599b752 Binary files /dev/null and b/static/tiles/8/156/102.png differ diff --git a/static/tiles/8/156/103.png b/static/tiles/8/156/103.png new file mode 100644 index 0000000..02ef1f0 Binary files /dev/null and b/static/tiles/8/156/103.png differ diff --git a/static/tiles/8/156/104.png b/static/tiles/8/156/104.png new file mode 100644 index 0000000..9ac82dd Binary files /dev/null and b/static/tiles/8/156/104.png differ diff --git a/static/tiles/8/156/105.png b/static/tiles/8/156/105.png new file mode 100644 index 0000000..f233847 Binary files /dev/null and b/static/tiles/8/156/105.png differ diff --git a/static/tiles/8/156/106.png b/static/tiles/8/156/106.png new file mode 100644 index 0000000..1a5fd15 Binary files /dev/null and b/static/tiles/8/156/106.png differ diff --git a/static/tiles/8/156/75.png b/static/tiles/8/156/75.png new file mode 100644 index 0000000..2328dca Binary files /dev/null and b/static/tiles/8/156/75.png differ diff --git a/static/tiles/8/156/76.png b/static/tiles/8/156/76.png new file mode 100644 index 0000000..62586be Binary files /dev/null and b/static/tiles/8/156/76.png differ diff --git a/static/tiles/8/156/77.png b/static/tiles/8/156/77.png new file mode 100644 index 0000000..a67a9db Binary files /dev/null and b/static/tiles/8/156/77.png differ diff --git a/static/tiles/8/156/78.png b/static/tiles/8/156/78.png new file mode 100644 index 0000000..2f1a08b Binary files /dev/null and b/static/tiles/8/156/78.png differ diff --git a/static/tiles/8/156/79.png b/static/tiles/8/156/79.png new file mode 100644 index 0000000..e8db910 Binary files /dev/null and b/static/tiles/8/156/79.png differ diff --git a/static/tiles/8/156/80.png b/static/tiles/8/156/80.png new file mode 100644 index 0000000..7e2698d Binary files /dev/null and b/static/tiles/8/156/80.png differ diff --git a/static/tiles/8/156/81.png b/static/tiles/8/156/81.png new file mode 100644 index 0000000..47d9758 Binary files /dev/null and b/static/tiles/8/156/81.png differ diff --git a/static/tiles/8/156/82.png b/static/tiles/8/156/82.png new file mode 100644 index 0000000..429feea Binary files /dev/null and b/static/tiles/8/156/82.png differ diff --git a/static/tiles/8/156/83.png b/static/tiles/8/156/83.png new file mode 100644 index 0000000..c824686 Binary files /dev/null and b/static/tiles/8/156/83.png differ diff --git a/static/tiles/8/156/84.png b/static/tiles/8/156/84.png new file mode 100644 index 0000000..2c92fa5 Binary files /dev/null and b/static/tiles/8/156/84.png differ diff --git a/static/tiles/8/156/85.png b/static/tiles/8/156/85.png new file mode 100644 index 0000000..fe1a253 Binary files /dev/null and b/static/tiles/8/156/85.png differ diff --git a/static/tiles/8/156/86.png b/static/tiles/8/156/86.png new file mode 100644 index 0000000..0d9751b Binary files /dev/null and b/static/tiles/8/156/86.png differ diff --git a/static/tiles/8/156/87.png b/static/tiles/8/156/87.png new file mode 100644 index 0000000..4d0ad51 Binary files /dev/null and b/static/tiles/8/156/87.png differ diff --git a/static/tiles/8/156/88.png b/static/tiles/8/156/88.png new file mode 100644 index 0000000..4ea6135 Binary files /dev/null and b/static/tiles/8/156/88.png differ diff --git a/static/tiles/8/156/89.png b/static/tiles/8/156/89.png new file mode 100644 index 0000000..b8cc477 Binary files /dev/null and b/static/tiles/8/156/89.png differ diff --git a/static/tiles/8/156/90.png b/static/tiles/8/156/90.png new file mode 100644 index 0000000..1355dd3 Binary files /dev/null and b/static/tiles/8/156/90.png differ diff --git a/static/tiles/8/156/91.png b/static/tiles/8/156/91.png new file mode 100644 index 0000000..3413005 Binary files /dev/null and b/static/tiles/8/156/91.png differ diff --git a/static/tiles/8/156/92.png b/static/tiles/8/156/92.png new file mode 100644 index 0000000..ebff766 Binary files /dev/null and b/static/tiles/8/156/92.png differ diff --git a/static/tiles/8/156/93.png b/static/tiles/8/156/93.png new file mode 100644 index 0000000..187a409 Binary files /dev/null and b/static/tiles/8/156/93.png differ diff --git a/static/tiles/8/156/94.png b/static/tiles/8/156/94.png new file mode 100644 index 0000000..462533b Binary files /dev/null and b/static/tiles/8/156/94.png differ diff --git a/static/tiles/8/156/95.png b/static/tiles/8/156/95.png new file mode 100644 index 0000000..a8e6e6b Binary files /dev/null and b/static/tiles/8/156/95.png differ diff --git a/static/tiles/8/156/96.png b/static/tiles/8/156/96.png new file mode 100644 index 0000000..bdefb17 Binary files /dev/null and b/static/tiles/8/156/96.png differ diff --git a/static/tiles/8/156/97.png b/static/tiles/8/156/97.png new file mode 100644 index 0000000..b7fb764 Binary files /dev/null and b/static/tiles/8/156/97.png differ diff --git a/static/tiles/8/156/98.png b/static/tiles/8/156/98.png new file mode 100644 index 0000000..ecbffd1 Binary files /dev/null and b/static/tiles/8/156/98.png differ diff --git a/static/tiles/8/156/99.png b/static/tiles/8/156/99.png new file mode 100644 index 0000000..231eac8 Binary files /dev/null and b/static/tiles/8/156/99.png differ diff --git a/static/tiles/8/157/100.png b/static/tiles/8/157/100.png new file mode 100644 index 0000000..bd528ce Binary files /dev/null and b/static/tiles/8/157/100.png differ diff --git a/static/tiles/8/157/101.png b/static/tiles/8/157/101.png new file mode 100644 index 0000000..a6820e1 Binary files /dev/null and b/static/tiles/8/157/101.png differ diff --git a/static/tiles/8/157/102.png b/static/tiles/8/157/102.png new file mode 100644 index 0000000..6329866 Binary files /dev/null and b/static/tiles/8/157/102.png differ diff --git a/static/tiles/8/157/103.png b/static/tiles/8/157/103.png new file mode 100644 index 0000000..712df6f Binary files /dev/null and b/static/tiles/8/157/103.png differ diff --git a/static/tiles/8/157/104.png b/static/tiles/8/157/104.png new file mode 100644 index 0000000..dd4fd4e Binary files /dev/null and b/static/tiles/8/157/104.png differ diff --git a/static/tiles/8/157/105.png b/static/tiles/8/157/105.png new file mode 100644 index 0000000..364633d Binary files /dev/null and b/static/tiles/8/157/105.png differ diff --git a/static/tiles/8/157/106.png b/static/tiles/8/157/106.png new file mode 100644 index 0000000..a505433 Binary files /dev/null and b/static/tiles/8/157/106.png differ diff --git a/static/tiles/8/157/75.png b/static/tiles/8/157/75.png new file mode 100644 index 0000000..bb3d8b0 Binary files /dev/null and b/static/tiles/8/157/75.png differ diff --git a/static/tiles/8/157/76.png b/static/tiles/8/157/76.png new file mode 100644 index 0000000..db6b6c8 Binary files /dev/null and b/static/tiles/8/157/76.png differ diff --git a/static/tiles/8/157/77.png b/static/tiles/8/157/77.png new file mode 100644 index 0000000..cdd353e Binary files /dev/null and b/static/tiles/8/157/77.png differ diff --git a/static/tiles/8/157/78.png b/static/tiles/8/157/78.png new file mode 100644 index 0000000..a8b5b6d Binary files /dev/null and b/static/tiles/8/157/78.png differ diff --git a/static/tiles/8/157/79.png b/static/tiles/8/157/79.png new file mode 100644 index 0000000..6404758 Binary files /dev/null and b/static/tiles/8/157/79.png differ diff --git a/static/tiles/8/157/80.png b/static/tiles/8/157/80.png new file mode 100644 index 0000000..487424d Binary files /dev/null and b/static/tiles/8/157/80.png differ diff --git a/static/tiles/8/157/81.png b/static/tiles/8/157/81.png new file mode 100644 index 0000000..bcbec94 Binary files /dev/null and b/static/tiles/8/157/81.png differ diff --git a/static/tiles/8/157/82.png b/static/tiles/8/157/82.png new file mode 100644 index 0000000..1f04312 Binary files /dev/null and b/static/tiles/8/157/82.png differ diff --git a/static/tiles/8/157/83.png b/static/tiles/8/157/83.png new file mode 100644 index 0000000..cf56e1d Binary files /dev/null and b/static/tiles/8/157/83.png differ diff --git a/static/tiles/8/157/84.png b/static/tiles/8/157/84.png new file mode 100644 index 0000000..c8d62ea Binary files /dev/null and b/static/tiles/8/157/84.png differ diff --git a/static/tiles/8/157/85.png b/static/tiles/8/157/85.png new file mode 100644 index 0000000..26d3850 Binary files /dev/null and b/static/tiles/8/157/85.png differ diff --git a/static/tiles/8/157/86.png b/static/tiles/8/157/86.png new file mode 100644 index 0000000..feae014 Binary files /dev/null and b/static/tiles/8/157/86.png differ diff --git a/static/tiles/8/157/87.png b/static/tiles/8/157/87.png new file mode 100644 index 0000000..9f3c94b Binary files /dev/null and b/static/tiles/8/157/87.png differ diff --git a/static/tiles/8/157/88.png b/static/tiles/8/157/88.png new file mode 100644 index 0000000..efcf9a6 Binary files /dev/null and b/static/tiles/8/157/88.png differ diff --git a/static/tiles/8/157/89.png b/static/tiles/8/157/89.png new file mode 100644 index 0000000..a2d9921 Binary files /dev/null and b/static/tiles/8/157/89.png differ diff --git a/static/tiles/8/157/90.png b/static/tiles/8/157/90.png new file mode 100644 index 0000000..7dfdcb9 Binary files /dev/null and b/static/tiles/8/157/90.png differ diff --git a/static/tiles/8/157/91.png b/static/tiles/8/157/91.png new file mode 100644 index 0000000..00f76b0 Binary files /dev/null and b/static/tiles/8/157/91.png differ diff --git a/static/tiles/8/157/92.png b/static/tiles/8/157/92.png new file mode 100644 index 0000000..7fcd06a Binary files /dev/null and b/static/tiles/8/157/92.png differ diff --git a/static/tiles/8/157/93.png b/static/tiles/8/157/93.png new file mode 100644 index 0000000..2fe7c94 Binary files /dev/null and b/static/tiles/8/157/93.png differ diff --git a/static/tiles/8/157/94.png b/static/tiles/8/157/94.png new file mode 100644 index 0000000..a3c7b86 Binary files /dev/null and b/static/tiles/8/157/94.png differ diff --git a/static/tiles/8/157/95.png b/static/tiles/8/157/95.png new file mode 100644 index 0000000..3743d92 Binary files /dev/null and b/static/tiles/8/157/95.png differ diff --git a/static/tiles/8/157/96.png b/static/tiles/8/157/96.png new file mode 100644 index 0000000..f73872c Binary files /dev/null and b/static/tiles/8/157/96.png differ diff --git a/static/tiles/8/157/97.png b/static/tiles/8/157/97.png new file mode 100644 index 0000000..5c4be45 Binary files /dev/null and b/static/tiles/8/157/97.png differ diff --git a/static/tiles/8/157/98.png b/static/tiles/8/157/98.png new file mode 100644 index 0000000..056e0ae Binary files /dev/null and b/static/tiles/8/157/98.png differ diff --git a/static/tiles/8/157/99.png b/static/tiles/8/157/99.png new file mode 100644 index 0000000..54ea65c Binary files /dev/null and b/static/tiles/8/157/99.png differ diff --git a/static/tiles/8/158/100.png b/static/tiles/8/158/100.png new file mode 100644 index 0000000..5ce84e5 Binary files /dev/null and b/static/tiles/8/158/100.png differ diff --git a/static/tiles/8/158/101.png b/static/tiles/8/158/101.png new file mode 100644 index 0000000..71c4f5f Binary files /dev/null and b/static/tiles/8/158/101.png differ diff --git a/static/tiles/8/158/102.png b/static/tiles/8/158/102.png new file mode 100644 index 0000000..b77bec3 Binary files /dev/null and b/static/tiles/8/158/102.png differ diff --git a/static/tiles/8/158/103.png b/static/tiles/8/158/103.png new file mode 100644 index 0000000..2ee513c Binary files /dev/null and b/static/tiles/8/158/103.png differ diff --git a/static/tiles/8/158/104.png b/static/tiles/8/158/104.png new file mode 100644 index 0000000..4274ecd Binary files /dev/null and b/static/tiles/8/158/104.png differ diff --git a/static/tiles/8/158/105.png b/static/tiles/8/158/105.png new file mode 100644 index 0000000..002afde Binary files /dev/null and b/static/tiles/8/158/105.png differ diff --git a/static/tiles/8/158/106.png b/static/tiles/8/158/106.png new file mode 100644 index 0000000..846c702 Binary files /dev/null and b/static/tiles/8/158/106.png differ diff --git a/static/tiles/8/158/75.png b/static/tiles/8/158/75.png new file mode 100644 index 0000000..1be5452 Binary files /dev/null and b/static/tiles/8/158/75.png differ diff --git a/static/tiles/8/158/76.png b/static/tiles/8/158/76.png new file mode 100644 index 0000000..50b6b23 Binary files /dev/null and b/static/tiles/8/158/76.png differ diff --git a/static/tiles/8/158/77.png b/static/tiles/8/158/77.png new file mode 100644 index 0000000..eea0f09 Binary files /dev/null and b/static/tiles/8/158/77.png differ diff --git a/static/tiles/8/158/78.png b/static/tiles/8/158/78.png new file mode 100644 index 0000000..03f3277 Binary files /dev/null and b/static/tiles/8/158/78.png differ diff --git a/static/tiles/8/158/79.png b/static/tiles/8/158/79.png new file mode 100644 index 0000000..00c88df Binary files /dev/null and b/static/tiles/8/158/79.png differ diff --git a/static/tiles/8/158/80.png b/static/tiles/8/158/80.png new file mode 100644 index 0000000..f0d659e Binary files /dev/null and b/static/tiles/8/158/80.png differ diff --git a/static/tiles/8/158/81.png b/static/tiles/8/158/81.png new file mode 100644 index 0000000..26b230f Binary files /dev/null and b/static/tiles/8/158/81.png differ diff --git a/static/tiles/8/158/82.png b/static/tiles/8/158/82.png new file mode 100644 index 0000000..8186d1c Binary files /dev/null and b/static/tiles/8/158/82.png differ diff --git a/static/tiles/8/158/83.png b/static/tiles/8/158/83.png new file mode 100644 index 0000000..3745792 Binary files /dev/null and b/static/tiles/8/158/83.png differ diff --git a/static/tiles/8/158/84.png b/static/tiles/8/158/84.png new file mode 100644 index 0000000..1f48fe8 Binary files /dev/null and b/static/tiles/8/158/84.png differ diff --git a/static/tiles/8/158/85.png b/static/tiles/8/158/85.png new file mode 100644 index 0000000..b49b7af Binary files /dev/null and b/static/tiles/8/158/85.png differ diff --git a/static/tiles/8/158/86.png b/static/tiles/8/158/86.png new file mode 100644 index 0000000..50c4ed5 Binary files /dev/null and b/static/tiles/8/158/86.png differ diff --git a/static/tiles/8/158/87.png b/static/tiles/8/158/87.png new file mode 100644 index 0000000..25641c9 Binary files /dev/null and b/static/tiles/8/158/87.png differ diff --git a/static/tiles/8/158/88.png b/static/tiles/8/158/88.png new file mode 100644 index 0000000..d60cf19 Binary files /dev/null and b/static/tiles/8/158/88.png differ diff --git a/static/tiles/8/158/89.png b/static/tiles/8/158/89.png new file mode 100644 index 0000000..1ee3d4f Binary files /dev/null and b/static/tiles/8/158/89.png differ diff --git a/static/tiles/8/158/90.png b/static/tiles/8/158/90.png new file mode 100644 index 0000000..9ff81dd Binary files /dev/null and b/static/tiles/8/158/90.png differ diff --git a/static/tiles/8/158/91.png b/static/tiles/8/158/91.png new file mode 100644 index 0000000..72fc44c Binary files /dev/null and b/static/tiles/8/158/91.png differ diff --git a/static/tiles/8/158/92.png b/static/tiles/8/158/92.png new file mode 100644 index 0000000..4a64fd3 Binary files /dev/null and b/static/tiles/8/158/92.png differ diff --git a/static/tiles/8/158/93.png b/static/tiles/8/158/93.png new file mode 100644 index 0000000..dcd48d5 Binary files /dev/null and b/static/tiles/8/158/93.png differ diff --git a/static/tiles/8/158/94.png b/static/tiles/8/158/94.png new file mode 100644 index 0000000..18ce31f Binary files /dev/null and b/static/tiles/8/158/94.png differ diff --git a/static/tiles/8/158/95.png b/static/tiles/8/158/95.png new file mode 100644 index 0000000..88df36f Binary files /dev/null and b/static/tiles/8/158/95.png differ diff --git a/static/tiles/8/158/96.png b/static/tiles/8/158/96.png new file mode 100644 index 0000000..2cb9aa2 Binary files /dev/null and b/static/tiles/8/158/96.png differ diff --git a/static/tiles/8/158/97.png b/static/tiles/8/158/97.png new file mode 100644 index 0000000..b0dd534 Binary files /dev/null and b/static/tiles/8/158/97.png differ diff --git a/static/tiles/8/158/98.png b/static/tiles/8/158/98.png new file mode 100644 index 0000000..c4d0bcc Binary files /dev/null and b/static/tiles/8/158/98.png differ diff --git a/static/tiles/8/158/99.png b/static/tiles/8/158/99.png new file mode 100644 index 0000000..35c6f66 Binary files /dev/null and b/static/tiles/8/158/99.png differ diff --git a/static/tiles/8/159/100.png b/static/tiles/8/159/100.png new file mode 100644 index 0000000..a0687c3 Binary files /dev/null and b/static/tiles/8/159/100.png differ diff --git a/static/tiles/8/159/101.png b/static/tiles/8/159/101.png new file mode 100644 index 0000000..14aabd2 Binary files /dev/null and b/static/tiles/8/159/101.png differ diff --git a/static/tiles/8/159/102.png b/static/tiles/8/159/102.png new file mode 100644 index 0000000..c433794 Binary files /dev/null and b/static/tiles/8/159/102.png differ diff --git a/static/tiles/8/159/103.png b/static/tiles/8/159/103.png new file mode 100644 index 0000000..a1398fe Binary files /dev/null and b/static/tiles/8/159/103.png differ diff --git a/static/tiles/8/159/104.png b/static/tiles/8/159/104.png new file mode 100644 index 0000000..8b32f55 Binary files /dev/null and b/static/tiles/8/159/104.png differ diff --git a/static/tiles/8/159/105.png b/static/tiles/8/159/105.png new file mode 100644 index 0000000..ab0c47d Binary files /dev/null and b/static/tiles/8/159/105.png differ diff --git a/static/tiles/8/159/106.png b/static/tiles/8/159/106.png new file mode 100644 index 0000000..706c450 Binary files /dev/null and b/static/tiles/8/159/106.png differ diff --git a/static/tiles/8/159/75.png b/static/tiles/8/159/75.png new file mode 100644 index 0000000..386ad23 Binary files /dev/null and b/static/tiles/8/159/75.png differ diff --git a/static/tiles/8/159/76.png b/static/tiles/8/159/76.png new file mode 100644 index 0000000..d5bc2ae Binary files /dev/null and b/static/tiles/8/159/76.png differ diff --git a/static/tiles/8/159/77.png b/static/tiles/8/159/77.png new file mode 100644 index 0000000..89ba57f Binary files /dev/null and b/static/tiles/8/159/77.png differ diff --git a/static/tiles/8/159/78.png b/static/tiles/8/159/78.png new file mode 100644 index 0000000..3e21acd Binary files /dev/null and b/static/tiles/8/159/78.png differ diff --git a/static/tiles/8/159/79.png b/static/tiles/8/159/79.png new file mode 100644 index 0000000..1701bd7 Binary files /dev/null and b/static/tiles/8/159/79.png differ diff --git a/static/tiles/8/159/80.png b/static/tiles/8/159/80.png new file mode 100644 index 0000000..4d30cd0 Binary files /dev/null and b/static/tiles/8/159/80.png differ diff --git a/static/tiles/8/159/81.png b/static/tiles/8/159/81.png new file mode 100644 index 0000000..b094a95 Binary files /dev/null and b/static/tiles/8/159/81.png differ diff --git a/static/tiles/8/159/82.png b/static/tiles/8/159/82.png new file mode 100644 index 0000000..fdfa0a1 Binary files /dev/null and b/static/tiles/8/159/82.png differ diff --git a/static/tiles/8/159/83.png b/static/tiles/8/159/83.png new file mode 100644 index 0000000..1e7ce72 Binary files /dev/null and b/static/tiles/8/159/83.png differ diff --git a/static/tiles/8/159/84.png b/static/tiles/8/159/84.png new file mode 100644 index 0000000..aa8955e Binary files /dev/null and b/static/tiles/8/159/84.png differ diff --git a/static/tiles/8/159/85.png b/static/tiles/8/159/85.png new file mode 100644 index 0000000..8fd2571 Binary files /dev/null and b/static/tiles/8/159/85.png differ diff --git a/static/tiles/8/159/86.png b/static/tiles/8/159/86.png new file mode 100644 index 0000000..8a3ab56 Binary files /dev/null and b/static/tiles/8/159/86.png differ diff --git a/static/tiles/8/159/87.png b/static/tiles/8/159/87.png new file mode 100644 index 0000000..b7dcaed Binary files /dev/null and b/static/tiles/8/159/87.png differ diff --git a/static/tiles/8/159/88.png b/static/tiles/8/159/88.png new file mode 100644 index 0000000..8d9cabe Binary files /dev/null and b/static/tiles/8/159/88.png differ diff --git a/static/tiles/8/159/89.png b/static/tiles/8/159/89.png new file mode 100644 index 0000000..44aa6be Binary files /dev/null and b/static/tiles/8/159/89.png differ diff --git a/static/tiles/8/159/90.png b/static/tiles/8/159/90.png new file mode 100644 index 0000000..32749f4 Binary files /dev/null and b/static/tiles/8/159/90.png differ diff --git a/static/tiles/8/159/91.png b/static/tiles/8/159/91.png new file mode 100644 index 0000000..ea33b58 Binary files /dev/null and b/static/tiles/8/159/91.png differ diff --git a/static/tiles/8/159/92.png b/static/tiles/8/159/92.png new file mode 100644 index 0000000..4a2683b Binary files /dev/null and b/static/tiles/8/159/92.png differ diff --git a/static/tiles/8/159/93.png b/static/tiles/8/159/93.png new file mode 100644 index 0000000..0fd24db Binary files /dev/null and b/static/tiles/8/159/93.png differ diff --git a/static/tiles/8/159/94.png b/static/tiles/8/159/94.png new file mode 100644 index 0000000..0517842 Binary files /dev/null and b/static/tiles/8/159/94.png differ diff --git a/static/tiles/8/159/95.png b/static/tiles/8/159/95.png new file mode 100644 index 0000000..1a6c86b Binary files /dev/null and b/static/tiles/8/159/95.png differ diff --git a/static/tiles/8/159/96.png b/static/tiles/8/159/96.png new file mode 100644 index 0000000..165cdf2 Binary files /dev/null and b/static/tiles/8/159/96.png differ diff --git a/static/tiles/8/159/97.png b/static/tiles/8/159/97.png new file mode 100644 index 0000000..3984c80 Binary files /dev/null and b/static/tiles/8/159/97.png differ diff --git a/static/tiles/8/159/98.png b/static/tiles/8/159/98.png new file mode 100644 index 0000000..c92d114 Binary files /dev/null and b/static/tiles/8/159/98.png differ diff --git a/static/tiles/8/159/99.png b/static/tiles/8/159/99.png new file mode 100644 index 0000000..d841e46 Binary files /dev/null and b/static/tiles/8/159/99.png differ diff --git a/static/tiles/8/160/100.png b/static/tiles/8/160/100.png new file mode 100644 index 0000000..32125fc Binary files /dev/null and b/static/tiles/8/160/100.png differ diff --git a/static/tiles/8/160/101.png b/static/tiles/8/160/101.png new file mode 100644 index 0000000..2c98c99 Binary files /dev/null and b/static/tiles/8/160/101.png differ diff --git a/static/tiles/8/160/102.png b/static/tiles/8/160/102.png new file mode 100644 index 0000000..0bf6a4a Binary files /dev/null and b/static/tiles/8/160/102.png differ diff --git a/static/tiles/8/160/103.png b/static/tiles/8/160/103.png new file mode 100644 index 0000000..56fdd2e Binary files /dev/null and b/static/tiles/8/160/103.png differ diff --git a/static/tiles/8/160/104.png b/static/tiles/8/160/104.png new file mode 100644 index 0000000..6864d96 Binary files /dev/null and b/static/tiles/8/160/104.png differ diff --git a/static/tiles/8/160/105.png b/static/tiles/8/160/105.png new file mode 100644 index 0000000..745754f Binary files /dev/null and b/static/tiles/8/160/105.png differ diff --git a/static/tiles/8/160/106.png b/static/tiles/8/160/106.png new file mode 100644 index 0000000..0ffdc12 Binary files /dev/null and b/static/tiles/8/160/106.png differ diff --git a/static/tiles/8/160/75.png b/static/tiles/8/160/75.png new file mode 100644 index 0000000..e28ee50 Binary files /dev/null and b/static/tiles/8/160/75.png differ diff --git a/static/tiles/8/160/76.png b/static/tiles/8/160/76.png new file mode 100644 index 0000000..6112d9e Binary files /dev/null and b/static/tiles/8/160/76.png differ diff --git a/static/tiles/8/160/77.png b/static/tiles/8/160/77.png new file mode 100644 index 0000000..f58e6cb Binary files /dev/null and b/static/tiles/8/160/77.png differ diff --git a/static/tiles/8/160/78.png b/static/tiles/8/160/78.png new file mode 100644 index 0000000..a43e600 Binary files /dev/null and b/static/tiles/8/160/78.png differ diff --git a/static/tiles/8/160/79.png b/static/tiles/8/160/79.png new file mode 100644 index 0000000..fbca7d2 Binary files /dev/null and b/static/tiles/8/160/79.png differ diff --git a/static/tiles/8/160/80.png b/static/tiles/8/160/80.png new file mode 100644 index 0000000..b7a1753 Binary files /dev/null and b/static/tiles/8/160/80.png differ diff --git a/static/tiles/8/160/81.png b/static/tiles/8/160/81.png new file mode 100644 index 0000000..de253ce Binary files /dev/null and b/static/tiles/8/160/81.png differ diff --git a/static/tiles/8/160/82.png b/static/tiles/8/160/82.png new file mode 100644 index 0000000..145fbf6 Binary files /dev/null and b/static/tiles/8/160/82.png differ diff --git a/static/tiles/8/160/83.png b/static/tiles/8/160/83.png new file mode 100644 index 0000000..11ef374 Binary files /dev/null and b/static/tiles/8/160/83.png differ diff --git a/static/tiles/8/160/84.png b/static/tiles/8/160/84.png new file mode 100644 index 0000000..bf754d1 Binary files /dev/null and b/static/tiles/8/160/84.png differ diff --git a/static/tiles/8/160/85.png b/static/tiles/8/160/85.png new file mode 100644 index 0000000..b01bce1 Binary files /dev/null and b/static/tiles/8/160/85.png differ diff --git a/static/tiles/8/160/86.png b/static/tiles/8/160/86.png new file mode 100644 index 0000000..2f5190f Binary files /dev/null and b/static/tiles/8/160/86.png differ diff --git a/static/tiles/8/160/87.png b/static/tiles/8/160/87.png new file mode 100644 index 0000000..8395ea8 Binary files /dev/null and b/static/tiles/8/160/87.png differ diff --git a/static/tiles/8/160/88.png b/static/tiles/8/160/88.png new file mode 100644 index 0000000..247cf79 Binary files /dev/null and b/static/tiles/8/160/88.png differ diff --git a/static/tiles/8/160/89.png b/static/tiles/8/160/89.png new file mode 100644 index 0000000..614f4a0 Binary files /dev/null and b/static/tiles/8/160/89.png differ diff --git a/static/tiles/8/160/90.png b/static/tiles/8/160/90.png new file mode 100644 index 0000000..b5056f0 Binary files /dev/null and b/static/tiles/8/160/90.png differ diff --git a/static/tiles/8/160/91.png b/static/tiles/8/160/91.png new file mode 100644 index 0000000..fce0a8d Binary files /dev/null and b/static/tiles/8/160/91.png differ diff --git a/static/tiles/8/160/92.png b/static/tiles/8/160/92.png new file mode 100644 index 0000000..f1b9385 Binary files /dev/null and b/static/tiles/8/160/92.png differ diff --git a/static/tiles/8/160/93.png b/static/tiles/8/160/93.png new file mode 100644 index 0000000..726dd3a Binary files /dev/null and b/static/tiles/8/160/93.png differ diff --git a/static/tiles/8/160/94.png b/static/tiles/8/160/94.png new file mode 100644 index 0000000..fc739b9 Binary files /dev/null and b/static/tiles/8/160/94.png differ diff --git a/static/tiles/8/160/95.png b/static/tiles/8/160/95.png new file mode 100644 index 0000000..084eb56 Binary files /dev/null and b/static/tiles/8/160/95.png differ diff --git a/static/tiles/8/160/96.png b/static/tiles/8/160/96.png new file mode 100644 index 0000000..3ea532a Binary files /dev/null and b/static/tiles/8/160/96.png differ diff --git a/static/tiles/8/160/97.png b/static/tiles/8/160/97.png new file mode 100644 index 0000000..21bc57e Binary files /dev/null and b/static/tiles/8/160/97.png differ diff --git a/static/tiles/8/160/98.png b/static/tiles/8/160/98.png new file mode 100644 index 0000000..7861272 Binary files /dev/null and b/static/tiles/8/160/98.png differ diff --git a/static/tiles/8/160/99.png b/static/tiles/8/160/99.png new file mode 100644 index 0000000..078ee37 Binary files /dev/null and b/static/tiles/8/160/99.png differ diff --git a/static/tiles/8/161/100.png b/static/tiles/8/161/100.png new file mode 100644 index 0000000..9bb9834 Binary files /dev/null and b/static/tiles/8/161/100.png differ diff --git a/static/tiles/8/161/101.png b/static/tiles/8/161/101.png new file mode 100644 index 0000000..ef5155b Binary files /dev/null and b/static/tiles/8/161/101.png differ diff --git a/static/tiles/8/161/102.png b/static/tiles/8/161/102.png new file mode 100644 index 0000000..963f309 Binary files /dev/null and b/static/tiles/8/161/102.png differ diff --git a/static/tiles/8/161/103.png b/static/tiles/8/161/103.png new file mode 100644 index 0000000..8537f44 Binary files /dev/null and b/static/tiles/8/161/103.png differ diff --git a/static/tiles/8/161/104.png b/static/tiles/8/161/104.png new file mode 100644 index 0000000..c2176f6 Binary files /dev/null and b/static/tiles/8/161/104.png differ diff --git a/static/tiles/8/161/105.png b/static/tiles/8/161/105.png new file mode 100644 index 0000000..fd5c9dd Binary files /dev/null and b/static/tiles/8/161/105.png differ diff --git a/static/tiles/8/161/106.png b/static/tiles/8/161/106.png new file mode 100644 index 0000000..b215efe Binary files /dev/null and b/static/tiles/8/161/106.png differ diff --git a/static/tiles/8/161/75.png b/static/tiles/8/161/75.png new file mode 100644 index 0000000..70b2ed6 Binary files /dev/null and b/static/tiles/8/161/75.png differ diff --git a/static/tiles/8/161/76.png b/static/tiles/8/161/76.png new file mode 100644 index 0000000..2841234 Binary files /dev/null and b/static/tiles/8/161/76.png differ diff --git a/static/tiles/8/161/77.png b/static/tiles/8/161/77.png new file mode 100644 index 0000000..e38e944 Binary files /dev/null and b/static/tiles/8/161/77.png differ diff --git a/static/tiles/8/161/78.png b/static/tiles/8/161/78.png new file mode 100644 index 0000000..ea65acf Binary files /dev/null and b/static/tiles/8/161/78.png differ diff --git a/static/tiles/8/161/79.png b/static/tiles/8/161/79.png new file mode 100644 index 0000000..e857d24 Binary files /dev/null and b/static/tiles/8/161/79.png differ diff --git a/static/tiles/8/161/80.png b/static/tiles/8/161/80.png new file mode 100644 index 0000000..bdaf885 Binary files /dev/null and b/static/tiles/8/161/80.png differ diff --git a/static/tiles/8/161/81.png b/static/tiles/8/161/81.png new file mode 100644 index 0000000..f195cab Binary files /dev/null and b/static/tiles/8/161/81.png differ diff --git a/static/tiles/8/161/82.png b/static/tiles/8/161/82.png new file mode 100644 index 0000000..f4d1345 Binary files /dev/null and b/static/tiles/8/161/82.png differ diff --git a/static/tiles/8/161/83.png b/static/tiles/8/161/83.png new file mode 100644 index 0000000..6573712 Binary files /dev/null and b/static/tiles/8/161/83.png differ diff --git a/static/tiles/8/161/84.png b/static/tiles/8/161/84.png new file mode 100644 index 0000000..32696da Binary files /dev/null and b/static/tiles/8/161/84.png differ diff --git a/static/tiles/8/161/85.png b/static/tiles/8/161/85.png new file mode 100644 index 0000000..cab73f7 Binary files /dev/null and b/static/tiles/8/161/85.png differ diff --git a/static/tiles/8/161/86.png b/static/tiles/8/161/86.png new file mode 100644 index 0000000..0de5008 Binary files /dev/null and b/static/tiles/8/161/86.png differ diff --git a/static/tiles/8/161/87.png b/static/tiles/8/161/87.png new file mode 100644 index 0000000..065e7e7 Binary files /dev/null and b/static/tiles/8/161/87.png differ diff --git a/static/tiles/8/161/88.png b/static/tiles/8/161/88.png new file mode 100644 index 0000000..723af2f Binary files /dev/null and b/static/tiles/8/161/88.png differ diff --git a/static/tiles/8/161/89.png b/static/tiles/8/161/89.png new file mode 100644 index 0000000..35bf043 Binary files /dev/null and b/static/tiles/8/161/89.png differ diff --git a/static/tiles/8/161/90.png b/static/tiles/8/161/90.png new file mode 100644 index 0000000..70e2ab6 Binary files /dev/null and b/static/tiles/8/161/90.png differ diff --git a/static/tiles/8/161/91.png b/static/tiles/8/161/91.png new file mode 100644 index 0000000..de5692a Binary files /dev/null and b/static/tiles/8/161/91.png differ diff --git a/static/tiles/8/161/92.png b/static/tiles/8/161/92.png new file mode 100644 index 0000000..77e9cc6 Binary files /dev/null and b/static/tiles/8/161/92.png differ diff --git a/static/tiles/8/161/93.png b/static/tiles/8/161/93.png new file mode 100644 index 0000000..523d644 Binary files /dev/null and b/static/tiles/8/161/93.png differ diff --git a/static/tiles/8/161/94.png b/static/tiles/8/161/94.png new file mode 100644 index 0000000..d0b784c Binary files /dev/null and b/static/tiles/8/161/94.png differ diff --git a/static/tiles/8/161/95.png b/static/tiles/8/161/95.png new file mode 100644 index 0000000..524643d Binary files /dev/null and b/static/tiles/8/161/95.png differ diff --git a/static/tiles/8/161/96.png b/static/tiles/8/161/96.png new file mode 100644 index 0000000..3db5bcc Binary files /dev/null and b/static/tiles/8/161/96.png differ diff --git a/static/tiles/8/161/97.png b/static/tiles/8/161/97.png new file mode 100644 index 0000000..b89cb9a Binary files /dev/null and b/static/tiles/8/161/97.png differ diff --git a/static/tiles/8/161/98.png b/static/tiles/8/161/98.png new file mode 100644 index 0000000..69e4bfb Binary files /dev/null and b/static/tiles/8/161/98.png differ diff --git a/static/tiles/8/161/99.png b/static/tiles/8/161/99.png new file mode 100644 index 0000000..a25e362 Binary files /dev/null and b/static/tiles/8/161/99.png differ diff --git a/static/tiles/8/162/100.png b/static/tiles/8/162/100.png new file mode 100644 index 0000000..5585f75 Binary files /dev/null and b/static/tiles/8/162/100.png differ diff --git a/static/tiles/8/162/101.png b/static/tiles/8/162/101.png new file mode 100644 index 0000000..414e55c Binary files /dev/null and b/static/tiles/8/162/101.png differ diff --git a/static/tiles/8/162/102.png b/static/tiles/8/162/102.png new file mode 100644 index 0000000..dae5d9e Binary files /dev/null and b/static/tiles/8/162/102.png differ diff --git a/static/tiles/8/162/103.png b/static/tiles/8/162/103.png new file mode 100644 index 0000000..1230179 Binary files /dev/null and b/static/tiles/8/162/103.png differ diff --git a/static/tiles/8/162/104.png b/static/tiles/8/162/104.png new file mode 100644 index 0000000..d149502 Binary files /dev/null and b/static/tiles/8/162/104.png differ diff --git a/static/tiles/8/162/105.png b/static/tiles/8/162/105.png new file mode 100644 index 0000000..d25cf27 Binary files /dev/null and b/static/tiles/8/162/105.png differ diff --git a/static/tiles/8/162/106.png b/static/tiles/8/162/106.png new file mode 100644 index 0000000..9ff0097 Binary files /dev/null and b/static/tiles/8/162/106.png differ diff --git a/static/tiles/8/162/75.png b/static/tiles/8/162/75.png new file mode 100644 index 0000000..9a23508 Binary files /dev/null and b/static/tiles/8/162/75.png differ diff --git a/static/tiles/8/162/76.png b/static/tiles/8/162/76.png new file mode 100644 index 0000000..7fd1728 Binary files /dev/null and b/static/tiles/8/162/76.png differ diff --git a/static/tiles/8/162/77.png b/static/tiles/8/162/77.png new file mode 100644 index 0000000..0acd22d Binary files /dev/null and b/static/tiles/8/162/77.png differ diff --git a/static/tiles/8/162/78.png b/static/tiles/8/162/78.png new file mode 100644 index 0000000..6ec791d Binary files /dev/null and b/static/tiles/8/162/78.png differ diff --git a/static/tiles/8/162/79.png b/static/tiles/8/162/79.png new file mode 100644 index 0000000..340ab65 Binary files /dev/null and b/static/tiles/8/162/79.png differ diff --git a/static/tiles/8/162/80.png b/static/tiles/8/162/80.png new file mode 100644 index 0000000..4caac53 Binary files /dev/null and b/static/tiles/8/162/80.png differ diff --git a/static/tiles/8/162/81.png b/static/tiles/8/162/81.png new file mode 100644 index 0000000..82fe546 Binary files /dev/null and b/static/tiles/8/162/81.png differ diff --git a/static/tiles/8/162/82.png b/static/tiles/8/162/82.png new file mode 100644 index 0000000..cbba306 Binary files /dev/null and b/static/tiles/8/162/82.png differ diff --git a/static/tiles/8/162/83.png b/static/tiles/8/162/83.png new file mode 100644 index 0000000..4ab2dad Binary files /dev/null and b/static/tiles/8/162/83.png differ diff --git a/static/tiles/8/162/84.png b/static/tiles/8/162/84.png new file mode 100644 index 0000000..461c69c Binary files /dev/null and b/static/tiles/8/162/84.png differ diff --git a/static/tiles/8/162/85.png b/static/tiles/8/162/85.png new file mode 100644 index 0000000..900864a Binary files /dev/null and b/static/tiles/8/162/85.png differ diff --git a/static/tiles/8/162/86.png b/static/tiles/8/162/86.png new file mode 100644 index 0000000..9df03fe Binary files /dev/null and b/static/tiles/8/162/86.png differ diff --git a/static/tiles/8/162/87.png b/static/tiles/8/162/87.png new file mode 100644 index 0000000..0310d0e Binary files /dev/null and b/static/tiles/8/162/87.png differ diff --git a/static/tiles/8/162/88.png b/static/tiles/8/162/88.png new file mode 100644 index 0000000..73ea281 Binary files /dev/null and b/static/tiles/8/162/88.png differ diff --git a/static/tiles/8/162/89.png b/static/tiles/8/162/89.png new file mode 100644 index 0000000..09b5902 Binary files /dev/null and b/static/tiles/8/162/89.png differ diff --git a/static/tiles/8/162/90.png b/static/tiles/8/162/90.png new file mode 100644 index 0000000..b85ff00 Binary files /dev/null and b/static/tiles/8/162/90.png differ diff --git a/static/tiles/8/162/91.png b/static/tiles/8/162/91.png new file mode 100644 index 0000000..bbc8f67 Binary files /dev/null and b/static/tiles/8/162/91.png differ diff --git a/static/tiles/8/162/92.png b/static/tiles/8/162/92.png new file mode 100644 index 0000000..a8f2c95 Binary files /dev/null and b/static/tiles/8/162/92.png differ diff --git a/static/tiles/8/162/93.png b/static/tiles/8/162/93.png new file mode 100644 index 0000000..8127aa5 Binary files /dev/null and b/static/tiles/8/162/93.png differ diff --git a/static/tiles/8/162/94.png b/static/tiles/8/162/94.png new file mode 100644 index 0000000..dde245d Binary files /dev/null and b/static/tiles/8/162/94.png differ diff --git a/static/tiles/8/162/95.png b/static/tiles/8/162/95.png new file mode 100644 index 0000000..d2e26fb Binary files /dev/null and b/static/tiles/8/162/95.png differ diff --git a/static/tiles/8/162/96.png b/static/tiles/8/162/96.png new file mode 100644 index 0000000..d16f642 Binary files /dev/null and b/static/tiles/8/162/96.png differ diff --git a/static/tiles/8/162/97.png b/static/tiles/8/162/97.png new file mode 100644 index 0000000..d52c322 Binary files /dev/null and b/static/tiles/8/162/97.png differ diff --git a/static/tiles/8/162/98.png b/static/tiles/8/162/98.png new file mode 100644 index 0000000..c740bbe Binary files /dev/null and b/static/tiles/8/162/98.png differ diff --git a/static/tiles/8/162/99.png b/static/tiles/8/162/99.png new file mode 100644 index 0000000..720e716 Binary files /dev/null and b/static/tiles/8/162/99.png differ diff --git a/static/tiles/8/163/100.png b/static/tiles/8/163/100.png new file mode 100644 index 0000000..063d3a2 Binary files /dev/null and b/static/tiles/8/163/100.png differ diff --git a/static/tiles/8/163/101.png b/static/tiles/8/163/101.png new file mode 100644 index 0000000..3df042e Binary files /dev/null and b/static/tiles/8/163/101.png differ diff --git a/static/tiles/8/163/102.png b/static/tiles/8/163/102.png new file mode 100644 index 0000000..cc830c7 Binary files /dev/null and b/static/tiles/8/163/102.png differ diff --git a/static/tiles/8/163/103.png b/static/tiles/8/163/103.png new file mode 100644 index 0000000..7ca8d84 Binary files /dev/null and b/static/tiles/8/163/103.png differ diff --git a/static/tiles/8/163/104.png b/static/tiles/8/163/104.png new file mode 100644 index 0000000..143772c Binary files /dev/null and b/static/tiles/8/163/104.png differ diff --git a/static/tiles/8/163/105.png b/static/tiles/8/163/105.png new file mode 100644 index 0000000..0680256 Binary files /dev/null and b/static/tiles/8/163/105.png differ diff --git a/static/tiles/8/163/106.png b/static/tiles/8/163/106.png new file mode 100644 index 0000000..92f774c Binary files /dev/null and b/static/tiles/8/163/106.png differ diff --git a/static/tiles/8/163/75.png b/static/tiles/8/163/75.png new file mode 100644 index 0000000..33be873 Binary files /dev/null and b/static/tiles/8/163/75.png differ diff --git a/static/tiles/8/163/76.png b/static/tiles/8/163/76.png new file mode 100644 index 0000000..ccc01cc Binary files /dev/null and b/static/tiles/8/163/76.png differ diff --git a/static/tiles/8/163/77.png b/static/tiles/8/163/77.png new file mode 100644 index 0000000..77d2347 Binary files /dev/null and b/static/tiles/8/163/77.png differ diff --git a/static/tiles/8/163/78.png b/static/tiles/8/163/78.png new file mode 100644 index 0000000..95c4742 Binary files /dev/null and b/static/tiles/8/163/78.png differ diff --git a/static/tiles/8/163/79.png b/static/tiles/8/163/79.png new file mode 100644 index 0000000..a337546 Binary files /dev/null and b/static/tiles/8/163/79.png differ diff --git a/static/tiles/8/163/80.png b/static/tiles/8/163/80.png new file mode 100644 index 0000000..7dff124 Binary files /dev/null and b/static/tiles/8/163/80.png differ diff --git a/static/tiles/8/163/81.png b/static/tiles/8/163/81.png new file mode 100644 index 0000000..39a3960 Binary files /dev/null and b/static/tiles/8/163/81.png differ diff --git a/static/tiles/8/163/82.png b/static/tiles/8/163/82.png new file mode 100644 index 0000000..1b312bc Binary files /dev/null and b/static/tiles/8/163/82.png differ diff --git a/static/tiles/8/163/83.png b/static/tiles/8/163/83.png new file mode 100644 index 0000000..ea1031d Binary files /dev/null and b/static/tiles/8/163/83.png differ diff --git a/static/tiles/8/163/84.png b/static/tiles/8/163/84.png new file mode 100644 index 0000000..c8e038d Binary files /dev/null and b/static/tiles/8/163/84.png differ diff --git a/static/tiles/8/163/85.png b/static/tiles/8/163/85.png new file mode 100644 index 0000000..3fa7812 Binary files /dev/null and b/static/tiles/8/163/85.png differ diff --git a/static/tiles/8/163/86.png b/static/tiles/8/163/86.png new file mode 100644 index 0000000..2516472 Binary files /dev/null and b/static/tiles/8/163/86.png differ diff --git a/static/tiles/8/163/87.png b/static/tiles/8/163/87.png new file mode 100644 index 0000000..07693a4 Binary files /dev/null and b/static/tiles/8/163/87.png differ diff --git a/static/tiles/8/163/88.png b/static/tiles/8/163/88.png new file mode 100644 index 0000000..6387162 Binary files /dev/null and b/static/tiles/8/163/88.png differ diff --git a/static/tiles/8/163/89.png b/static/tiles/8/163/89.png new file mode 100644 index 0000000..ce73657 Binary files /dev/null and b/static/tiles/8/163/89.png differ diff --git a/static/tiles/8/163/90.png b/static/tiles/8/163/90.png new file mode 100644 index 0000000..24ddc1b Binary files /dev/null and b/static/tiles/8/163/90.png differ diff --git a/static/tiles/8/163/91.png b/static/tiles/8/163/91.png new file mode 100644 index 0000000..27edbdc Binary files /dev/null and b/static/tiles/8/163/91.png differ diff --git a/static/tiles/8/163/92.png b/static/tiles/8/163/92.png new file mode 100644 index 0000000..9214b7c Binary files /dev/null and b/static/tiles/8/163/92.png differ diff --git a/static/tiles/8/163/93.png b/static/tiles/8/163/93.png new file mode 100644 index 0000000..2691a20 Binary files /dev/null and b/static/tiles/8/163/93.png differ diff --git a/static/tiles/8/163/94.png b/static/tiles/8/163/94.png new file mode 100644 index 0000000..5c33feb Binary files /dev/null and b/static/tiles/8/163/94.png differ diff --git a/static/tiles/8/163/95.png b/static/tiles/8/163/95.png new file mode 100644 index 0000000..171aea8 Binary files /dev/null and b/static/tiles/8/163/95.png differ diff --git a/static/tiles/8/163/96.png b/static/tiles/8/163/96.png new file mode 100644 index 0000000..6180393 Binary files /dev/null and b/static/tiles/8/163/96.png differ diff --git a/static/tiles/8/163/97.png b/static/tiles/8/163/97.png new file mode 100644 index 0000000..d9d2ad8 Binary files /dev/null and b/static/tiles/8/163/97.png differ diff --git a/static/tiles/8/163/98.png b/static/tiles/8/163/98.png new file mode 100644 index 0000000..c431969 Binary files /dev/null and b/static/tiles/8/163/98.png differ diff --git a/static/tiles/8/163/99.png b/static/tiles/8/163/99.png new file mode 100644 index 0000000..f014d46 Binary files /dev/null and b/static/tiles/8/163/99.png differ diff --git a/static/tiles/8/164/100.png b/static/tiles/8/164/100.png new file mode 100644 index 0000000..594cdf4 Binary files /dev/null and b/static/tiles/8/164/100.png differ diff --git a/static/tiles/8/164/101.png b/static/tiles/8/164/101.png new file mode 100644 index 0000000..236e3d5 Binary files /dev/null and b/static/tiles/8/164/101.png differ diff --git a/static/tiles/8/164/102.png b/static/tiles/8/164/102.png new file mode 100644 index 0000000..a99aa31 Binary files /dev/null and b/static/tiles/8/164/102.png differ diff --git a/static/tiles/8/164/103.png b/static/tiles/8/164/103.png new file mode 100644 index 0000000..90c8b6e Binary files /dev/null and b/static/tiles/8/164/103.png differ diff --git a/static/tiles/8/164/104.png b/static/tiles/8/164/104.png new file mode 100644 index 0000000..15afb9c Binary files /dev/null and b/static/tiles/8/164/104.png differ diff --git a/static/tiles/8/164/105.png b/static/tiles/8/164/105.png new file mode 100644 index 0000000..16d8910 Binary files /dev/null and b/static/tiles/8/164/105.png differ diff --git a/static/tiles/8/164/106.png b/static/tiles/8/164/106.png new file mode 100644 index 0000000..e8329ab Binary files /dev/null and b/static/tiles/8/164/106.png differ diff --git a/static/tiles/8/164/75.png b/static/tiles/8/164/75.png new file mode 100644 index 0000000..09a5cf1 Binary files /dev/null and b/static/tiles/8/164/75.png differ diff --git a/static/tiles/8/164/76.png b/static/tiles/8/164/76.png new file mode 100644 index 0000000..8b41fc9 Binary files /dev/null and b/static/tiles/8/164/76.png differ diff --git a/static/tiles/8/164/77.png b/static/tiles/8/164/77.png new file mode 100644 index 0000000..1b3800d Binary files /dev/null and b/static/tiles/8/164/77.png differ diff --git a/static/tiles/8/164/78.png b/static/tiles/8/164/78.png new file mode 100644 index 0000000..542b218 Binary files /dev/null and b/static/tiles/8/164/78.png differ diff --git a/static/tiles/8/164/79.png b/static/tiles/8/164/79.png new file mode 100644 index 0000000..ddba79a Binary files /dev/null and b/static/tiles/8/164/79.png differ diff --git a/static/tiles/8/164/80.png b/static/tiles/8/164/80.png new file mode 100644 index 0000000..ef554c2 Binary files /dev/null and b/static/tiles/8/164/80.png differ diff --git a/static/tiles/8/164/81.png b/static/tiles/8/164/81.png new file mode 100644 index 0000000..277531f Binary files /dev/null and b/static/tiles/8/164/81.png differ diff --git a/static/tiles/8/164/82.png b/static/tiles/8/164/82.png new file mode 100644 index 0000000..e460676 Binary files /dev/null and b/static/tiles/8/164/82.png differ diff --git a/static/tiles/8/164/83.png b/static/tiles/8/164/83.png new file mode 100644 index 0000000..19c7ce9 Binary files /dev/null and b/static/tiles/8/164/83.png differ diff --git a/static/tiles/8/164/84.png b/static/tiles/8/164/84.png new file mode 100644 index 0000000..306ffa4 Binary files /dev/null and b/static/tiles/8/164/84.png differ diff --git a/static/tiles/8/164/85.png b/static/tiles/8/164/85.png new file mode 100644 index 0000000..9f37cfe Binary files /dev/null and b/static/tiles/8/164/85.png differ diff --git a/static/tiles/8/164/86.png b/static/tiles/8/164/86.png new file mode 100644 index 0000000..14783f1 Binary files /dev/null and b/static/tiles/8/164/86.png differ diff --git a/static/tiles/8/164/87.png b/static/tiles/8/164/87.png new file mode 100644 index 0000000..6f9b1f4 Binary files /dev/null and b/static/tiles/8/164/87.png differ diff --git a/static/tiles/8/164/88.png b/static/tiles/8/164/88.png new file mode 100644 index 0000000..1db80cf Binary files /dev/null and b/static/tiles/8/164/88.png differ diff --git a/static/tiles/8/164/89.png b/static/tiles/8/164/89.png new file mode 100644 index 0000000..ab9eb39 Binary files /dev/null and b/static/tiles/8/164/89.png differ diff --git a/static/tiles/8/164/90.png b/static/tiles/8/164/90.png new file mode 100644 index 0000000..5c6a266 Binary files /dev/null and b/static/tiles/8/164/90.png differ diff --git a/static/tiles/8/164/91.png b/static/tiles/8/164/91.png new file mode 100644 index 0000000..7c7e81a Binary files /dev/null and b/static/tiles/8/164/91.png differ diff --git a/static/tiles/8/164/92.png b/static/tiles/8/164/92.png new file mode 100644 index 0000000..72a1344 Binary files /dev/null and b/static/tiles/8/164/92.png differ diff --git a/static/tiles/8/164/93.png b/static/tiles/8/164/93.png new file mode 100644 index 0000000..11c55c2 Binary files /dev/null and b/static/tiles/8/164/93.png differ diff --git a/static/tiles/8/164/94.png b/static/tiles/8/164/94.png new file mode 100644 index 0000000..9a3ea36 Binary files /dev/null and b/static/tiles/8/164/94.png differ diff --git a/static/tiles/8/164/95.png b/static/tiles/8/164/95.png new file mode 100644 index 0000000..d105c77 Binary files /dev/null and b/static/tiles/8/164/95.png differ diff --git a/static/tiles/8/164/96.png b/static/tiles/8/164/96.png new file mode 100644 index 0000000..ec185b8 Binary files /dev/null and b/static/tiles/8/164/96.png differ diff --git a/static/tiles/8/164/97.png b/static/tiles/8/164/97.png new file mode 100644 index 0000000..1eb8ca2 Binary files /dev/null and b/static/tiles/8/164/97.png differ diff --git a/static/tiles/8/164/98.png b/static/tiles/8/164/98.png new file mode 100644 index 0000000..f607ae0 Binary files /dev/null and b/static/tiles/8/164/98.png differ diff --git a/static/tiles/8/164/99.png b/static/tiles/8/164/99.png new file mode 100644 index 0000000..fd53e86 Binary files /dev/null and b/static/tiles/8/164/99.png differ diff --git a/static/tiles/8/165/100.png b/static/tiles/8/165/100.png new file mode 100644 index 0000000..a5e349a Binary files /dev/null and b/static/tiles/8/165/100.png differ diff --git a/static/tiles/8/165/101.png b/static/tiles/8/165/101.png new file mode 100644 index 0000000..c4f78c4 Binary files /dev/null and b/static/tiles/8/165/101.png differ diff --git a/static/tiles/8/165/102.png b/static/tiles/8/165/102.png new file mode 100644 index 0000000..348f0d5 Binary files /dev/null and b/static/tiles/8/165/102.png differ diff --git a/static/tiles/8/165/103.png b/static/tiles/8/165/103.png new file mode 100644 index 0000000..c573426 Binary files /dev/null and b/static/tiles/8/165/103.png differ diff --git a/static/tiles/8/165/104.png b/static/tiles/8/165/104.png new file mode 100644 index 0000000..116092d Binary files /dev/null and b/static/tiles/8/165/104.png differ diff --git a/static/tiles/8/165/105.png b/static/tiles/8/165/105.png new file mode 100644 index 0000000..6ce463b Binary files /dev/null and b/static/tiles/8/165/105.png differ diff --git a/static/tiles/8/165/106.png b/static/tiles/8/165/106.png new file mode 100644 index 0000000..069a245 Binary files /dev/null and b/static/tiles/8/165/106.png differ diff --git a/static/tiles/8/165/75.png b/static/tiles/8/165/75.png new file mode 100644 index 0000000..bd58c9f Binary files /dev/null and b/static/tiles/8/165/75.png differ diff --git a/static/tiles/8/165/76.png b/static/tiles/8/165/76.png new file mode 100644 index 0000000..1d6af52 Binary files /dev/null and b/static/tiles/8/165/76.png differ diff --git a/static/tiles/8/165/77.png b/static/tiles/8/165/77.png new file mode 100644 index 0000000..80d0918 Binary files /dev/null and b/static/tiles/8/165/77.png differ diff --git a/static/tiles/8/165/78.png b/static/tiles/8/165/78.png new file mode 100644 index 0000000..07d8572 Binary files /dev/null and b/static/tiles/8/165/78.png differ diff --git a/static/tiles/8/165/79.png b/static/tiles/8/165/79.png new file mode 100644 index 0000000..29ce0e5 Binary files /dev/null and b/static/tiles/8/165/79.png differ diff --git a/static/tiles/8/165/80.png b/static/tiles/8/165/80.png new file mode 100644 index 0000000..97e1477 Binary files /dev/null and b/static/tiles/8/165/80.png differ diff --git a/static/tiles/8/165/81.png b/static/tiles/8/165/81.png new file mode 100644 index 0000000..f573fb6 Binary files /dev/null and b/static/tiles/8/165/81.png differ diff --git a/static/tiles/8/165/82.png b/static/tiles/8/165/82.png new file mode 100644 index 0000000..bff45c8 Binary files /dev/null and b/static/tiles/8/165/82.png differ diff --git a/static/tiles/8/165/83.png b/static/tiles/8/165/83.png new file mode 100644 index 0000000..d7f6dbc Binary files /dev/null and b/static/tiles/8/165/83.png differ diff --git a/static/tiles/8/165/84.png b/static/tiles/8/165/84.png new file mode 100644 index 0000000..c5c656c Binary files /dev/null and b/static/tiles/8/165/84.png differ diff --git a/static/tiles/8/165/85.png b/static/tiles/8/165/85.png new file mode 100644 index 0000000..8ccd8e4 Binary files /dev/null and b/static/tiles/8/165/85.png differ diff --git a/static/tiles/8/165/86.png b/static/tiles/8/165/86.png new file mode 100644 index 0000000..a748a8e Binary files /dev/null and b/static/tiles/8/165/86.png differ diff --git a/static/tiles/8/165/87.png b/static/tiles/8/165/87.png new file mode 100644 index 0000000..5086392 Binary files /dev/null and b/static/tiles/8/165/87.png differ diff --git a/static/tiles/8/165/88.png b/static/tiles/8/165/88.png new file mode 100644 index 0000000..24756d0 Binary files /dev/null and b/static/tiles/8/165/88.png differ diff --git a/static/tiles/8/165/89.png b/static/tiles/8/165/89.png new file mode 100644 index 0000000..8087ebd Binary files /dev/null and b/static/tiles/8/165/89.png differ diff --git a/static/tiles/8/165/90.png b/static/tiles/8/165/90.png new file mode 100644 index 0000000..97847e2 Binary files /dev/null and b/static/tiles/8/165/90.png differ diff --git a/static/tiles/8/165/91.png b/static/tiles/8/165/91.png new file mode 100644 index 0000000..67f2cd5 Binary files /dev/null and b/static/tiles/8/165/91.png differ diff --git a/static/tiles/8/165/92.png b/static/tiles/8/165/92.png new file mode 100644 index 0000000..9f185c6 Binary files /dev/null and b/static/tiles/8/165/92.png differ diff --git a/static/tiles/8/165/93.png b/static/tiles/8/165/93.png new file mode 100644 index 0000000..d6f61d3 Binary files /dev/null and b/static/tiles/8/165/93.png differ diff --git a/static/tiles/8/165/94.png b/static/tiles/8/165/94.png new file mode 100644 index 0000000..37e36da Binary files /dev/null and b/static/tiles/8/165/94.png differ diff --git a/static/tiles/8/165/95.png b/static/tiles/8/165/95.png new file mode 100644 index 0000000..bfc090f Binary files /dev/null and b/static/tiles/8/165/95.png differ diff --git a/static/tiles/8/165/96.png b/static/tiles/8/165/96.png new file mode 100644 index 0000000..9d828b8 Binary files /dev/null and b/static/tiles/8/165/96.png differ diff --git a/static/tiles/8/165/97.png b/static/tiles/8/165/97.png new file mode 100644 index 0000000..2c5764f Binary files /dev/null and b/static/tiles/8/165/97.png differ diff --git a/static/tiles/8/165/98.png b/static/tiles/8/165/98.png new file mode 100644 index 0000000..961ad9a Binary files /dev/null and b/static/tiles/8/165/98.png differ diff --git a/static/tiles/8/165/99.png b/static/tiles/8/165/99.png new file mode 100644 index 0000000..0da3ca8 Binary files /dev/null and b/static/tiles/8/165/99.png differ diff --git a/static/tiles/8/166/100.png b/static/tiles/8/166/100.png new file mode 100644 index 0000000..41c6edc Binary files /dev/null and b/static/tiles/8/166/100.png differ diff --git a/static/tiles/8/166/101.png b/static/tiles/8/166/101.png new file mode 100644 index 0000000..92b5ac7 Binary files /dev/null and b/static/tiles/8/166/101.png differ diff --git a/static/tiles/8/166/102.png b/static/tiles/8/166/102.png new file mode 100644 index 0000000..0fb23bb Binary files /dev/null and b/static/tiles/8/166/102.png differ diff --git a/static/tiles/8/166/103.png b/static/tiles/8/166/103.png new file mode 100644 index 0000000..72c33a4 Binary files /dev/null and b/static/tiles/8/166/103.png differ diff --git a/static/tiles/8/166/104.png b/static/tiles/8/166/104.png new file mode 100644 index 0000000..72c6621 Binary files /dev/null and b/static/tiles/8/166/104.png differ diff --git a/static/tiles/8/166/105.png b/static/tiles/8/166/105.png new file mode 100644 index 0000000..659e871 Binary files /dev/null and b/static/tiles/8/166/105.png differ diff --git a/static/tiles/8/166/106.png b/static/tiles/8/166/106.png new file mode 100644 index 0000000..87c849b Binary files /dev/null and b/static/tiles/8/166/106.png differ diff --git a/static/tiles/8/166/75.png b/static/tiles/8/166/75.png new file mode 100644 index 0000000..02d4ad7 Binary files /dev/null and b/static/tiles/8/166/75.png differ diff --git a/static/tiles/8/166/76.png b/static/tiles/8/166/76.png new file mode 100644 index 0000000..c93ec61 Binary files /dev/null and b/static/tiles/8/166/76.png differ diff --git a/static/tiles/8/166/77.png b/static/tiles/8/166/77.png new file mode 100644 index 0000000..3b1d000 Binary files /dev/null and b/static/tiles/8/166/77.png differ diff --git a/static/tiles/8/166/78.png b/static/tiles/8/166/78.png new file mode 100644 index 0000000..ac23ba5 Binary files /dev/null and b/static/tiles/8/166/78.png differ diff --git a/static/tiles/8/166/79.png b/static/tiles/8/166/79.png new file mode 100644 index 0000000..4aa993d Binary files /dev/null and b/static/tiles/8/166/79.png differ diff --git a/static/tiles/8/166/80.png b/static/tiles/8/166/80.png new file mode 100644 index 0000000..546c06e Binary files /dev/null and b/static/tiles/8/166/80.png differ diff --git a/static/tiles/8/166/81.png b/static/tiles/8/166/81.png new file mode 100644 index 0000000..79c1861 Binary files /dev/null and b/static/tiles/8/166/81.png differ diff --git a/static/tiles/8/166/82.png b/static/tiles/8/166/82.png new file mode 100644 index 0000000..08e460d Binary files /dev/null and b/static/tiles/8/166/82.png differ diff --git a/static/tiles/8/166/83.png b/static/tiles/8/166/83.png new file mode 100644 index 0000000..a4cb0af Binary files /dev/null and b/static/tiles/8/166/83.png differ diff --git a/static/tiles/8/166/84.png b/static/tiles/8/166/84.png new file mode 100644 index 0000000..96923bf Binary files /dev/null and b/static/tiles/8/166/84.png differ diff --git a/static/tiles/8/166/85.png b/static/tiles/8/166/85.png new file mode 100644 index 0000000..410878d Binary files /dev/null and b/static/tiles/8/166/85.png differ diff --git a/static/tiles/8/166/86.png b/static/tiles/8/166/86.png new file mode 100644 index 0000000..86eb8c1 Binary files /dev/null and b/static/tiles/8/166/86.png differ diff --git a/static/tiles/8/166/87.png b/static/tiles/8/166/87.png new file mode 100644 index 0000000..9bc761a Binary files /dev/null and b/static/tiles/8/166/87.png differ diff --git a/static/tiles/8/166/88.png b/static/tiles/8/166/88.png new file mode 100644 index 0000000..23f1571 Binary files /dev/null and b/static/tiles/8/166/88.png differ diff --git a/static/tiles/8/166/89.png b/static/tiles/8/166/89.png new file mode 100644 index 0000000..521a0f6 Binary files /dev/null and b/static/tiles/8/166/89.png differ diff --git a/static/tiles/8/166/90.png b/static/tiles/8/166/90.png new file mode 100644 index 0000000..bc4beed Binary files /dev/null and b/static/tiles/8/166/90.png differ diff --git a/static/tiles/8/166/91.png b/static/tiles/8/166/91.png new file mode 100644 index 0000000..8b5cb9f Binary files /dev/null and b/static/tiles/8/166/91.png differ diff --git a/static/tiles/8/166/92.png b/static/tiles/8/166/92.png new file mode 100644 index 0000000..0acb682 Binary files /dev/null and b/static/tiles/8/166/92.png differ diff --git a/static/tiles/8/166/93.png b/static/tiles/8/166/93.png new file mode 100644 index 0000000..1896d1d Binary files /dev/null and b/static/tiles/8/166/93.png differ diff --git a/static/tiles/8/166/94.png b/static/tiles/8/166/94.png new file mode 100644 index 0000000..fdec795 Binary files /dev/null and b/static/tiles/8/166/94.png differ diff --git a/static/tiles/8/166/95.png b/static/tiles/8/166/95.png new file mode 100644 index 0000000..4e9e9fb Binary files /dev/null and b/static/tiles/8/166/95.png differ diff --git a/static/tiles/8/166/96.png b/static/tiles/8/166/96.png new file mode 100644 index 0000000..50a6eb6 Binary files /dev/null and b/static/tiles/8/166/96.png differ diff --git a/static/tiles/8/166/97.png b/static/tiles/8/166/97.png new file mode 100644 index 0000000..a238faa Binary files /dev/null and b/static/tiles/8/166/97.png differ diff --git a/static/tiles/8/166/98.png b/static/tiles/8/166/98.png new file mode 100644 index 0000000..abc8684 Binary files /dev/null and b/static/tiles/8/166/98.png differ diff --git a/static/tiles/8/166/99.png b/static/tiles/8/166/99.png new file mode 100644 index 0000000..b2013f1 Binary files /dev/null and b/static/tiles/8/166/99.png differ diff --git a/static/tiles/8/167/100.png b/static/tiles/8/167/100.png new file mode 100644 index 0000000..d9c69be Binary files /dev/null and b/static/tiles/8/167/100.png differ diff --git a/static/tiles/8/167/101.png b/static/tiles/8/167/101.png new file mode 100644 index 0000000..3ed704d Binary files /dev/null and b/static/tiles/8/167/101.png differ diff --git a/static/tiles/8/167/102.png b/static/tiles/8/167/102.png new file mode 100644 index 0000000..beacaa6 Binary files /dev/null and b/static/tiles/8/167/102.png differ diff --git a/static/tiles/8/167/103.png b/static/tiles/8/167/103.png new file mode 100644 index 0000000..16937f5 Binary files /dev/null and b/static/tiles/8/167/103.png differ diff --git a/static/tiles/8/167/104.png b/static/tiles/8/167/104.png new file mode 100644 index 0000000..f049982 Binary files /dev/null and b/static/tiles/8/167/104.png differ diff --git a/static/tiles/8/167/105.png b/static/tiles/8/167/105.png new file mode 100644 index 0000000..8997fc9 Binary files /dev/null and b/static/tiles/8/167/105.png differ diff --git a/static/tiles/8/167/106.png b/static/tiles/8/167/106.png new file mode 100644 index 0000000..eecac61 Binary files /dev/null and b/static/tiles/8/167/106.png differ diff --git a/static/tiles/8/167/75.png b/static/tiles/8/167/75.png new file mode 100644 index 0000000..5e3fcca Binary files /dev/null and b/static/tiles/8/167/75.png differ diff --git a/static/tiles/8/167/76.png b/static/tiles/8/167/76.png new file mode 100644 index 0000000..682a955 Binary files /dev/null and b/static/tiles/8/167/76.png differ diff --git a/static/tiles/8/167/77.png b/static/tiles/8/167/77.png new file mode 100644 index 0000000..4f3ac43 Binary files /dev/null and b/static/tiles/8/167/77.png differ diff --git a/static/tiles/8/167/78.png b/static/tiles/8/167/78.png new file mode 100644 index 0000000..d15a10c Binary files /dev/null and b/static/tiles/8/167/78.png differ diff --git a/static/tiles/8/167/79.png b/static/tiles/8/167/79.png new file mode 100644 index 0000000..26087f4 Binary files /dev/null and b/static/tiles/8/167/79.png differ diff --git a/static/tiles/8/167/80.png b/static/tiles/8/167/80.png new file mode 100644 index 0000000..2955da5 Binary files /dev/null and b/static/tiles/8/167/80.png differ diff --git a/static/tiles/8/167/81.png b/static/tiles/8/167/81.png new file mode 100644 index 0000000..f80421e Binary files /dev/null and b/static/tiles/8/167/81.png differ diff --git a/static/tiles/8/167/82.png b/static/tiles/8/167/82.png new file mode 100644 index 0000000..dfc29fc Binary files /dev/null and b/static/tiles/8/167/82.png differ diff --git a/static/tiles/8/167/83.png b/static/tiles/8/167/83.png new file mode 100644 index 0000000..46ed08d Binary files /dev/null and b/static/tiles/8/167/83.png differ diff --git a/static/tiles/8/167/84.png b/static/tiles/8/167/84.png new file mode 100644 index 0000000..335586d Binary files /dev/null and b/static/tiles/8/167/84.png differ diff --git a/static/tiles/8/167/85.png b/static/tiles/8/167/85.png new file mode 100644 index 0000000..e5de08e Binary files /dev/null and b/static/tiles/8/167/85.png differ diff --git a/static/tiles/8/167/86.png b/static/tiles/8/167/86.png new file mode 100644 index 0000000..d97390b Binary files /dev/null and b/static/tiles/8/167/86.png differ diff --git a/static/tiles/8/167/87.png b/static/tiles/8/167/87.png new file mode 100644 index 0000000..2be78be Binary files /dev/null and b/static/tiles/8/167/87.png differ diff --git a/static/tiles/8/167/88.png b/static/tiles/8/167/88.png new file mode 100644 index 0000000..bf7ab36 Binary files /dev/null and b/static/tiles/8/167/88.png differ diff --git a/static/tiles/8/167/89.png b/static/tiles/8/167/89.png new file mode 100644 index 0000000..f86a3dd Binary files /dev/null and b/static/tiles/8/167/89.png differ diff --git a/static/tiles/8/167/90.png b/static/tiles/8/167/90.png new file mode 100644 index 0000000..a3c1017 Binary files /dev/null and b/static/tiles/8/167/90.png differ diff --git a/static/tiles/8/167/91.png b/static/tiles/8/167/91.png new file mode 100644 index 0000000..6a315b9 Binary files /dev/null and b/static/tiles/8/167/91.png differ diff --git a/static/tiles/8/167/92.png b/static/tiles/8/167/92.png new file mode 100644 index 0000000..2634926 Binary files /dev/null and b/static/tiles/8/167/92.png differ diff --git a/static/tiles/8/167/93.png b/static/tiles/8/167/93.png new file mode 100644 index 0000000..bbb97b5 Binary files /dev/null and b/static/tiles/8/167/93.png differ diff --git a/static/tiles/8/167/94.png b/static/tiles/8/167/94.png new file mode 100644 index 0000000..51ddd58 Binary files /dev/null and b/static/tiles/8/167/94.png differ diff --git a/static/tiles/8/167/95.png b/static/tiles/8/167/95.png new file mode 100644 index 0000000..5f71461 Binary files /dev/null and b/static/tiles/8/167/95.png differ diff --git a/static/tiles/8/167/96.png b/static/tiles/8/167/96.png new file mode 100644 index 0000000..2e5bdaa Binary files /dev/null and b/static/tiles/8/167/96.png differ diff --git a/static/tiles/8/167/97.png b/static/tiles/8/167/97.png new file mode 100644 index 0000000..3085360 Binary files /dev/null and b/static/tiles/8/167/97.png differ diff --git a/static/tiles/8/167/98.png b/static/tiles/8/167/98.png new file mode 100644 index 0000000..de1c822 Binary files /dev/null and b/static/tiles/8/167/98.png differ diff --git a/static/tiles/8/167/99.png b/static/tiles/8/167/99.png new file mode 100644 index 0000000..6e3f9a6 Binary files /dev/null and b/static/tiles/8/167/99.png differ diff --git a/static/tiles/8/168/100.png b/static/tiles/8/168/100.png new file mode 100644 index 0000000..18fa835 Binary files /dev/null and b/static/tiles/8/168/100.png differ diff --git a/static/tiles/8/168/101.png b/static/tiles/8/168/101.png new file mode 100644 index 0000000..70b7134 Binary files /dev/null and b/static/tiles/8/168/101.png differ diff --git a/static/tiles/8/168/102.png b/static/tiles/8/168/102.png new file mode 100644 index 0000000..c67524c Binary files /dev/null and b/static/tiles/8/168/102.png differ diff --git a/static/tiles/8/168/103.png b/static/tiles/8/168/103.png new file mode 100644 index 0000000..b524be7 Binary files /dev/null and b/static/tiles/8/168/103.png differ diff --git a/static/tiles/8/168/104.png b/static/tiles/8/168/104.png new file mode 100644 index 0000000..064ec35 Binary files /dev/null and b/static/tiles/8/168/104.png differ diff --git a/static/tiles/8/168/105.png b/static/tiles/8/168/105.png new file mode 100644 index 0000000..024ecfb Binary files /dev/null and b/static/tiles/8/168/105.png differ diff --git a/static/tiles/8/168/106.png b/static/tiles/8/168/106.png new file mode 100644 index 0000000..a7dda07 Binary files /dev/null and b/static/tiles/8/168/106.png differ diff --git a/static/tiles/8/168/75.png b/static/tiles/8/168/75.png new file mode 100644 index 0000000..22bd0ba Binary files /dev/null and b/static/tiles/8/168/75.png differ diff --git a/static/tiles/8/168/76.png b/static/tiles/8/168/76.png new file mode 100644 index 0000000..ac5c5f7 Binary files /dev/null and b/static/tiles/8/168/76.png differ diff --git a/static/tiles/8/168/77.png b/static/tiles/8/168/77.png new file mode 100644 index 0000000..a4045d0 Binary files /dev/null and b/static/tiles/8/168/77.png differ diff --git a/static/tiles/8/168/78.png b/static/tiles/8/168/78.png new file mode 100644 index 0000000..f278afa Binary files /dev/null and b/static/tiles/8/168/78.png differ diff --git a/static/tiles/8/168/79.png b/static/tiles/8/168/79.png new file mode 100644 index 0000000..7ed3e5e Binary files /dev/null and b/static/tiles/8/168/79.png differ diff --git a/static/tiles/8/168/80.png b/static/tiles/8/168/80.png new file mode 100644 index 0000000..486dff8 Binary files /dev/null and b/static/tiles/8/168/80.png differ diff --git a/static/tiles/8/168/81.png b/static/tiles/8/168/81.png new file mode 100644 index 0000000..116329d Binary files /dev/null and b/static/tiles/8/168/81.png differ diff --git a/static/tiles/8/168/82.png b/static/tiles/8/168/82.png new file mode 100644 index 0000000..4224e4f Binary files /dev/null and b/static/tiles/8/168/82.png differ diff --git a/static/tiles/8/168/83.png b/static/tiles/8/168/83.png new file mode 100644 index 0000000..929d724 Binary files /dev/null and b/static/tiles/8/168/83.png differ diff --git a/static/tiles/8/168/84.png b/static/tiles/8/168/84.png new file mode 100644 index 0000000..86dec28 Binary files /dev/null and b/static/tiles/8/168/84.png differ diff --git a/static/tiles/8/168/85.png b/static/tiles/8/168/85.png new file mode 100644 index 0000000..5db895f Binary files /dev/null and b/static/tiles/8/168/85.png differ diff --git a/static/tiles/8/168/86.png b/static/tiles/8/168/86.png new file mode 100644 index 0000000..7a96bb4 Binary files /dev/null and b/static/tiles/8/168/86.png differ diff --git a/static/tiles/8/168/87.png b/static/tiles/8/168/87.png new file mode 100644 index 0000000..a13a06e Binary files /dev/null and b/static/tiles/8/168/87.png differ diff --git a/static/tiles/8/168/88.png b/static/tiles/8/168/88.png new file mode 100644 index 0000000..e240c22 Binary files /dev/null and b/static/tiles/8/168/88.png differ diff --git a/static/tiles/8/168/89.png b/static/tiles/8/168/89.png new file mode 100644 index 0000000..1c8d1ec Binary files /dev/null and b/static/tiles/8/168/89.png differ diff --git a/static/tiles/8/168/90.png b/static/tiles/8/168/90.png new file mode 100644 index 0000000..e656715 Binary files /dev/null and b/static/tiles/8/168/90.png differ diff --git a/static/tiles/8/168/91.png b/static/tiles/8/168/91.png new file mode 100644 index 0000000..cd7166f Binary files /dev/null and b/static/tiles/8/168/91.png differ diff --git a/static/tiles/8/168/92.png b/static/tiles/8/168/92.png new file mode 100644 index 0000000..c212238 Binary files /dev/null and b/static/tiles/8/168/92.png differ diff --git a/static/tiles/8/168/93.png b/static/tiles/8/168/93.png new file mode 100644 index 0000000..a9360be Binary files /dev/null and b/static/tiles/8/168/93.png differ diff --git a/static/tiles/8/168/94.png b/static/tiles/8/168/94.png new file mode 100644 index 0000000..96010ad Binary files /dev/null and b/static/tiles/8/168/94.png differ diff --git a/static/tiles/8/168/95.png b/static/tiles/8/168/95.png new file mode 100644 index 0000000..0795f85 Binary files /dev/null and b/static/tiles/8/168/95.png differ diff --git a/static/tiles/8/168/96.png b/static/tiles/8/168/96.png new file mode 100644 index 0000000..8ae5d33 Binary files /dev/null and b/static/tiles/8/168/96.png differ diff --git a/static/tiles/8/168/97.png b/static/tiles/8/168/97.png new file mode 100644 index 0000000..2e9a15a Binary files /dev/null and b/static/tiles/8/168/97.png differ diff --git a/static/tiles/8/168/98.png b/static/tiles/8/168/98.png new file mode 100644 index 0000000..5321e5c Binary files /dev/null and b/static/tiles/8/168/98.png differ diff --git a/static/tiles/8/168/99.png b/static/tiles/8/168/99.png new file mode 100644 index 0000000..a87d2d1 Binary files /dev/null and b/static/tiles/8/168/99.png differ diff --git a/static/tiles/8/169/100.png b/static/tiles/8/169/100.png new file mode 100644 index 0000000..3469aa8 Binary files /dev/null and b/static/tiles/8/169/100.png differ diff --git a/static/tiles/8/169/101.png b/static/tiles/8/169/101.png new file mode 100644 index 0000000..d66ed17 Binary files /dev/null and b/static/tiles/8/169/101.png differ diff --git a/static/tiles/8/169/102.png b/static/tiles/8/169/102.png new file mode 100644 index 0000000..b83c93b Binary files /dev/null and b/static/tiles/8/169/102.png differ diff --git a/static/tiles/8/169/103.png b/static/tiles/8/169/103.png new file mode 100644 index 0000000..28cdf30 Binary files /dev/null and b/static/tiles/8/169/103.png differ diff --git a/static/tiles/8/169/104.png b/static/tiles/8/169/104.png new file mode 100644 index 0000000..9f527c4 Binary files /dev/null and b/static/tiles/8/169/104.png differ diff --git a/static/tiles/8/169/105.png b/static/tiles/8/169/105.png new file mode 100644 index 0000000..64fccd5 Binary files /dev/null and b/static/tiles/8/169/105.png differ diff --git a/static/tiles/8/169/106.png b/static/tiles/8/169/106.png new file mode 100644 index 0000000..0259e5a Binary files /dev/null and b/static/tiles/8/169/106.png differ diff --git a/static/tiles/8/169/75.png b/static/tiles/8/169/75.png new file mode 100644 index 0000000..8e759bc Binary files /dev/null and b/static/tiles/8/169/75.png differ diff --git a/static/tiles/8/169/76.png b/static/tiles/8/169/76.png new file mode 100644 index 0000000..a46a647 Binary files /dev/null and b/static/tiles/8/169/76.png differ diff --git a/static/tiles/8/169/77.png b/static/tiles/8/169/77.png new file mode 100644 index 0000000..a905056 Binary files /dev/null and b/static/tiles/8/169/77.png differ diff --git a/static/tiles/8/169/78.png b/static/tiles/8/169/78.png new file mode 100644 index 0000000..bad3b18 Binary files /dev/null and b/static/tiles/8/169/78.png differ diff --git a/static/tiles/8/169/79.png b/static/tiles/8/169/79.png new file mode 100644 index 0000000..deb8c7b Binary files /dev/null and b/static/tiles/8/169/79.png differ diff --git a/static/tiles/8/169/80.png b/static/tiles/8/169/80.png new file mode 100644 index 0000000..50ed019 Binary files /dev/null and b/static/tiles/8/169/80.png differ diff --git a/static/tiles/8/169/81.png b/static/tiles/8/169/81.png new file mode 100644 index 0000000..daf9dc2 Binary files /dev/null and b/static/tiles/8/169/81.png differ diff --git a/static/tiles/8/169/82.png b/static/tiles/8/169/82.png new file mode 100644 index 0000000..ac883d5 Binary files /dev/null and b/static/tiles/8/169/82.png differ diff --git a/static/tiles/8/169/83.png b/static/tiles/8/169/83.png new file mode 100644 index 0000000..983ce03 Binary files /dev/null and b/static/tiles/8/169/83.png differ diff --git a/static/tiles/8/169/84.png b/static/tiles/8/169/84.png new file mode 100644 index 0000000..00a7565 Binary files /dev/null and b/static/tiles/8/169/84.png differ diff --git a/static/tiles/8/169/85.png b/static/tiles/8/169/85.png new file mode 100644 index 0000000..0684590 Binary files /dev/null and b/static/tiles/8/169/85.png differ diff --git a/static/tiles/8/169/86.png b/static/tiles/8/169/86.png new file mode 100644 index 0000000..4727a43 Binary files /dev/null and b/static/tiles/8/169/86.png differ diff --git a/static/tiles/8/169/87.png b/static/tiles/8/169/87.png new file mode 100644 index 0000000..b916131 Binary files /dev/null and b/static/tiles/8/169/87.png differ diff --git a/static/tiles/8/169/88.png b/static/tiles/8/169/88.png new file mode 100644 index 0000000..2be58bf Binary files /dev/null and b/static/tiles/8/169/88.png differ diff --git a/static/tiles/8/169/89.png b/static/tiles/8/169/89.png new file mode 100644 index 0000000..b935f9c Binary files /dev/null and b/static/tiles/8/169/89.png differ diff --git a/static/tiles/8/169/90.png b/static/tiles/8/169/90.png new file mode 100644 index 0000000..be992c9 Binary files /dev/null and b/static/tiles/8/169/90.png differ diff --git a/static/tiles/8/169/91.png b/static/tiles/8/169/91.png new file mode 100644 index 0000000..c3a48c7 Binary files /dev/null and b/static/tiles/8/169/91.png differ diff --git a/static/tiles/8/169/92.png b/static/tiles/8/169/92.png new file mode 100644 index 0000000..4dc0e7d Binary files /dev/null and b/static/tiles/8/169/92.png differ diff --git a/static/tiles/8/169/93.png b/static/tiles/8/169/93.png new file mode 100644 index 0000000..897a0ea Binary files /dev/null and b/static/tiles/8/169/93.png differ diff --git a/static/tiles/8/169/94.png b/static/tiles/8/169/94.png new file mode 100644 index 0000000..46b7681 Binary files /dev/null and b/static/tiles/8/169/94.png differ diff --git a/static/tiles/8/169/95.png b/static/tiles/8/169/95.png new file mode 100644 index 0000000..afb08c7 Binary files /dev/null and b/static/tiles/8/169/95.png differ diff --git a/static/tiles/8/169/96.png b/static/tiles/8/169/96.png new file mode 100644 index 0000000..57cdd80 Binary files /dev/null and b/static/tiles/8/169/96.png differ diff --git a/static/tiles/8/169/97.png b/static/tiles/8/169/97.png new file mode 100644 index 0000000..a9f5789 Binary files /dev/null and b/static/tiles/8/169/97.png differ diff --git a/static/tiles/8/169/98.png b/static/tiles/8/169/98.png new file mode 100644 index 0000000..2ae552d Binary files /dev/null and b/static/tiles/8/169/98.png differ diff --git a/static/tiles/8/169/99.png b/static/tiles/8/169/99.png new file mode 100644 index 0000000..2438fbf Binary files /dev/null and b/static/tiles/8/169/99.png differ diff --git a/static/tiles/8/170/100.png b/static/tiles/8/170/100.png new file mode 100644 index 0000000..a92792c Binary files /dev/null and b/static/tiles/8/170/100.png differ diff --git a/static/tiles/8/170/101.png b/static/tiles/8/170/101.png new file mode 100644 index 0000000..bc60f21 Binary files /dev/null and b/static/tiles/8/170/101.png differ diff --git a/static/tiles/8/170/102.png b/static/tiles/8/170/102.png new file mode 100644 index 0000000..fa57f91 Binary files /dev/null and b/static/tiles/8/170/102.png differ diff --git a/static/tiles/8/170/103.png b/static/tiles/8/170/103.png new file mode 100644 index 0000000..3222d07 Binary files /dev/null and b/static/tiles/8/170/103.png differ diff --git a/static/tiles/8/170/104.png b/static/tiles/8/170/104.png new file mode 100644 index 0000000..70898fc Binary files /dev/null and b/static/tiles/8/170/104.png differ diff --git a/static/tiles/8/170/105.png b/static/tiles/8/170/105.png new file mode 100644 index 0000000..fb22067 Binary files /dev/null and b/static/tiles/8/170/105.png differ diff --git a/static/tiles/8/170/106.png b/static/tiles/8/170/106.png new file mode 100644 index 0000000..8ae2132 Binary files /dev/null and b/static/tiles/8/170/106.png differ diff --git a/static/tiles/8/170/75.png b/static/tiles/8/170/75.png new file mode 100644 index 0000000..f0cdf9b Binary files /dev/null and b/static/tiles/8/170/75.png differ diff --git a/static/tiles/8/170/76.png b/static/tiles/8/170/76.png new file mode 100644 index 0000000..1b27ee1 Binary files /dev/null and b/static/tiles/8/170/76.png differ diff --git a/static/tiles/8/170/77.png b/static/tiles/8/170/77.png new file mode 100644 index 0000000..ec222fe Binary files /dev/null and b/static/tiles/8/170/77.png differ diff --git a/static/tiles/8/170/78.png b/static/tiles/8/170/78.png new file mode 100644 index 0000000..ba10ca7 Binary files /dev/null and b/static/tiles/8/170/78.png differ diff --git a/static/tiles/8/170/79.png b/static/tiles/8/170/79.png new file mode 100644 index 0000000..de14d42 Binary files /dev/null and b/static/tiles/8/170/79.png differ diff --git a/static/tiles/8/170/80.png b/static/tiles/8/170/80.png new file mode 100644 index 0000000..fe38626 Binary files /dev/null and b/static/tiles/8/170/80.png differ diff --git a/static/tiles/8/170/81.png b/static/tiles/8/170/81.png new file mode 100644 index 0000000..b452456 Binary files /dev/null and b/static/tiles/8/170/81.png differ diff --git a/static/tiles/8/170/82.png b/static/tiles/8/170/82.png new file mode 100644 index 0000000..5386465 Binary files /dev/null and b/static/tiles/8/170/82.png differ diff --git a/static/tiles/8/170/83.png b/static/tiles/8/170/83.png new file mode 100644 index 0000000..c71071e Binary files /dev/null and b/static/tiles/8/170/83.png differ diff --git a/static/tiles/8/170/84.png b/static/tiles/8/170/84.png new file mode 100644 index 0000000..7978341 Binary files /dev/null and b/static/tiles/8/170/84.png differ diff --git a/static/tiles/8/170/85.png b/static/tiles/8/170/85.png new file mode 100644 index 0000000..603546d Binary files /dev/null and b/static/tiles/8/170/85.png differ diff --git a/static/tiles/8/170/86.png b/static/tiles/8/170/86.png new file mode 100644 index 0000000..ef1f44b Binary files /dev/null and b/static/tiles/8/170/86.png differ diff --git a/static/tiles/8/170/87.png b/static/tiles/8/170/87.png new file mode 100644 index 0000000..ccaa7aa Binary files /dev/null and b/static/tiles/8/170/87.png differ diff --git a/static/tiles/8/170/88.png b/static/tiles/8/170/88.png new file mode 100644 index 0000000..bd4a0bc Binary files /dev/null and b/static/tiles/8/170/88.png differ diff --git a/static/tiles/8/170/89.png b/static/tiles/8/170/89.png new file mode 100644 index 0000000..e0897cb Binary files /dev/null and b/static/tiles/8/170/89.png differ diff --git a/static/tiles/8/170/90.png b/static/tiles/8/170/90.png new file mode 100644 index 0000000..5e9370a Binary files /dev/null and b/static/tiles/8/170/90.png differ diff --git a/static/tiles/8/170/91.png b/static/tiles/8/170/91.png new file mode 100644 index 0000000..bee7ee1 Binary files /dev/null and b/static/tiles/8/170/91.png differ diff --git a/static/tiles/8/170/92.png b/static/tiles/8/170/92.png new file mode 100644 index 0000000..8617e08 Binary files /dev/null and b/static/tiles/8/170/92.png differ diff --git a/static/tiles/8/170/93.png b/static/tiles/8/170/93.png new file mode 100644 index 0000000..cf989d5 Binary files /dev/null and b/static/tiles/8/170/93.png differ diff --git a/static/tiles/8/170/94.png b/static/tiles/8/170/94.png new file mode 100644 index 0000000..0fc8bc8 Binary files /dev/null and b/static/tiles/8/170/94.png differ diff --git a/static/tiles/8/170/95.png b/static/tiles/8/170/95.png new file mode 100644 index 0000000..b7025df Binary files /dev/null and b/static/tiles/8/170/95.png differ diff --git a/static/tiles/8/170/96.png b/static/tiles/8/170/96.png new file mode 100644 index 0000000..d916ea1 Binary files /dev/null and b/static/tiles/8/170/96.png differ diff --git a/static/tiles/8/170/97.png b/static/tiles/8/170/97.png new file mode 100644 index 0000000..4cbbca2 Binary files /dev/null and b/static/tiles/8/170/97.png differ diff --git a/static/tiles/8/170/98.png b/static/tiles/8/170/98.png new file mode 100644 index 0000000..92869c6 Binary files /dev/null and b/static/tiles/8/170/98.png differ diff --git a/static/tiles/8/170/99.png b/static/tiles/8/170/99.png new file mode 100644 index 0000000..b526c1a Binary files /dev/null and b/static/tiles/8/170/99.png differ diff --git a/static/tiles/8/171/100.png b/static/tiles/8/171/100.png new file mode 100644 index 0000000..c5fe800 Binary files /dev/null and b/static/tiles/8/171/100.png differ diff --git a/static/tiles/8/171/101.png b/static/tiles/8/171/101.png new file mode 100644 index 0000000..2124b07 Binary files /dev/null and b/static/tiles/8/171/101.png differ diff --git a/static/tiles/8/171/102.png b/static/tiles/8/171/102.png new file mode 100644 index 0000000..8ab42a2 Binary files /dev/null and b/static/tiles/8/171/102.png differ diff --git a/static/tiles/8/171/103.png b/static/tiles/8/171/103.png new file mode 100644 index 0000000..58e1e51 Binary files /dev/null and b/static/tiles/8/171/103.png differ diff --git a/static/tiles/8/171/104.png b/static/tiles/8/171/104.png new file mode 100644 index 0000000..93d5e69 Binary files /dev/null and b/static/tiles/8/171/104.png differ diff --git a/static/tiles/8/171/105.png b/static/tiles/8/171/105.png new file mode 100644 index 0000000..f45e731 Binary files /dev/null and b/static/tiles/8/171/105.png differ diff --git a/static/tiles/8/171/106.png b/static/tiles/8/171/106.png new file mode 100644 index 0000000..30b4d9a Binary files /dev/null and b/static/tiles/8/171/106.png differ diff --git a/static/tiles/8/171/75.png b/static/tiles/8/171/75.png new file mode 100644 index 0000000..c37bb6a Binary files /dev/null and b/static/tiles/8/171/75.png differ diff --git a/static/tiles/8/171/76.png b/static/tiles/8/171/76.png new file mode 100644 index 0000000..2d22e73 Binary files /dev/null and b/static/tiles/8/171/76.png differ diff --git a/static/tiles/8/171/77.png b/static/tiles/8/171/77.png new file mode 100644 index 0000000..4ac0330 Binary files /dev/null and b/static/tiles/8/171/77.png differ diff --git a/static/tiles/8/171/78.png b/static/tiles/8/171/78.png new file mode 100644 index 0000000..e3c348a Binary files /dev/null and b/static/tiles/8/171/78.png differ diff --git a/static/tiles/8/171/79.png b/static/tiles/8/171/79.png new file mode 100644 index 0000000..d830691 Binary files /dev/null and b/static/tiles/8/171/79.png differ diff --git a/static/tiles/8/171/80.png b/static/tiles/8/171/80.png new file mode 100644 index 0000000..1eb60eb Binary files /dev/null and b/static/tiles/8/171/80.png differ diff --git a/static/tiles/8/171/81.png b/static/tiles/8/171/81.png new file mode 100644 index 0000000..83ff6c2 Binary files /dev/null and b/static/tiles/8/171/81.png differ diff --git a/static/tiles/8/171/82.png b/static/tiles/8/171/82.png new file mode 100644 index 0000000..3dfc2d6 Binary files /dev/null and b/static/tiles/8/171/82.png differ diff --git a/static/tiles/8/171/83.png b/static/tiles/8/171/83.png new file mode 100644 index 0000000..08eed6b Binary files /dev/null and b/static/tiles/8/171/83.png differ diff --git a/static/tiles/8/171/84.png b/static/tiles/8/171/84.png new file mode 100644 index 0000000..85efe7d Binary files /dev/null and b/static/tiles/8/171/84.png differ diff --git a/static/tiles/8/171/85.png b/static/tiles/8/171/85.png new file mode 100644 index 0000000..980960f Binary files /dev/null and b/static/tiles/8/171/85.png differ diff --git a/static/tiles/8/171/86.png b/static/tiles/8/171/86.png new file mode 100644 index 0000000..51c9750 Binary files /dev/null and b/static/tiles/8/171/86.png differ diff --git a/static/tiles/8/171/87.png b/static/tiles/8/171/87.png new file mode 100644 index 0000000..c3eec30 Binary files /dev/null and b/static/tiles/8/171/87.png differ diff --git a/static/tiles/8/171/88.png b/static/tiles/8/171/88.png new file mode 100644 index 0000000..4201c19 Binary files /dev/null and b/static/tiles/8/171/88.png differ diff --git a/static/tiles/8/171/89.png b/static/tiles/8/171/89.png new file mode 100644 index 0000000..b6dd8b0 Binary files /dev/null and b/static/tiles/8/171/89.png differ diff --git a/static/tiles/8/171/90.png b/static/tiles/8/171/90.png new file mode 100644 index 0000000..986f80f Binary files /dev/null and b/static/tiles/8/171/90.png differ diff --git a/static/tiles/8/171/91.png b/static/tiles/8/171/91.png new file mode 100644 index 0000000..0aa684e Binary files /dev/null and b/static/tiles/8/171/91.png differ diff --git a/static/tiles/8/171/92.png b/static/tiles/8/171/92.png new file mode 100644 index 0000000..126db24 Binary files /dev/null and b/static/tiles/8/171/92.png differ diff --git a/static/tiles/8/171/93.png b/static/tiles/8/171/93.png new file mode 100644 index 0000000..fd00d12 Binary files /dev/null and b/static/tiles/8/171/93.png differ diff --git a/static/tiles/8/171/94.png b/static/tiles/8/171/94.png new file mode 100644 index 0000000..d6b8582 Binary files /dev/null and b/static/tiles/8/171/94.png differ diff --git a/static/tiles/8/171/95.png b/static/tiles/8/171/95.png new file mode 100644 index 0000000..bc0b6df Binary files /dev/null and b/static/tiles/8/171/95.png differ diff --git a/static/tiles/8/171/96.png b/static/tiles/8/171/96.png new file mode 100644 index 0000000..99d7ed4 Binary files /dev/null and b/static/tiles/8/171/96.png differ diff --git a/static/tiles/8/171/97.png b/static/tiles/8/171/97.png new file mode 100644 index 0000000..f8d1ef8 Binary files /dev/null and b/static/tiles/8/171/97.png differ diff --git a/static/tiles/8/171/98.png b/static/tiles/8/171/98.png new file mode 100644 index 0000000..102703b Binary files /dev/null and b/static/tiles/8/171/98.png differ diff --git a/static/tiles/8/171/99.png b/static/tiles/8/171/99.png new file mode 100644 index 0000000..f021b0b Binary files /dev/null and b/static/tiles/8/171/99.png differ diff --git a/static/tiles/8/172/100.png b/static/tiles/8/172/100.png new file mode 100644 index 0000000..391607f Binary files /dev/null and b/static/tiles/8/172/100.png differ diff --git a/static/tiles/8/172/101.png b/static/tiles/8/172/101.png new file mode 100644 index 0000000..fc0e891 Binary files /dev/null and b/static/tiles/8/172/101.png differ diff --git a/static/tiles/8/172/102.png b/static/tiles/8/172/102.png new file mode 100644 index 0000000..cda2fd2 Binary files /dev/null and b/static/tiles/8/172/102.png differ diff --git a/static/tiles/8/172/103.png b/static/tiles/8/172/103.png new file mode 100644 index 0000000..bb5bc61 Binary files /dev/null and b/static/tiles/8/172/103.png differ diff --git a/static/tiles/8/172/104.png b/static/tiles/8/172/104.png new file mode 100644 index 0000000..cb14173 Binary files /dev/null and b/static/tiles/8/172/104.png differ diff --git a/static/tiles/8/172/105.png b/static/tiles/8/172/105.png new file mode 100644 index 0000000..4cc8a24 Binary files /dev/null and b/static/tiles/8/172/105.png differ diff --git a/static/tiles/8/172/106.png b/static/tiles/8/172/106.png new file mode 100644 index 0000000..c26c1d9 Binary files /dev/null and b/static/tiles/8/172/106.png differ diff --git a/static/tiles/8/172/75.png b/static/tiles/8/172/75.png new file mode 100644 index 0000000..72fa555 Binary files /dev/null and b/static/tiles/8/172/75.png differ diff --git a/static/tiles/8/172/76.png b/static/tiles/8/172/76.png new file mode 100644 index 0000000..a32c0a8 Binary files /dev/null and b/static/tiles/8/172/76.png differ diff --git a/static/tiles/8/172/77.png b/static/tiles/8/172/77.png new file mode 100644 index 0000000..3e0c2d5 Binary files /dev/null and b/static/tiles/8/172/77.png differ diff --git a/static/tiles/8/172/78.png b/static/tiles/8/172/78.png new file mode 100644 index 0000000..799f622 Binary files /dev/null and b/static/tiles/8/172/78.png differ diff --git a/static/tiles/8/172/79.png b/static/tiles/8/172/79.png new file mode 100644 index 0000000..6d978aa Binary files /dev/null and b/static/tiles/8/172/79.png differ diff --git a/static/tiles/8/172/80.png b/static/tiles/8/172/80.png new file mode 100644 index 0000000..eb227d5 Binary files /dev/null and b/static/tiles/8/172/80.png differ diff --git a/static/tiles/8/172/81.png b/static/tiles/8/172/81.png new file mode 100644 index 0000000..ed2014a Binary files /dev/null and b/static/tiles/8/172/81.png differ diff --git a/static/tiles/8/172/82.png b/static/tiles/8/172/82.png new file mode 100644 index 0000000..9dcc2e5 Binary files /dev/null and b/static/tiles/8/172/82.png differ diff --git a/static/tiles/8/172/83.png b/static/tiles/8/172/83.png new file mode 100644 index 0000000..08f0ace Binary files /dev/null and b/static/tiles/8/172/83.png differ diff --git a/static/tiles/8/172/84.png b/static/tiles/8/172/84.png new file mode 100644 index 0000000..e68bbc2 Binary files /dev/null and b/static/tiles/8/172/84.png differ diff --git a/static/tiles/8/172/85.png b/static/tiles/8/172/85.png new file mode 100644 index 0000000..83d29e1 Binary files /dev/null and b/static/tiles/8/172/85.png differ diff --git a/static/tiles/8/172/86.png b/static/tiles/8/172/86.png new file mode 100644 index 0000000..77529f5 Binary files /dev/null and b/static/tiles/8/172/86.png differ diff --git a/static/tiles/8/172/87.png b/static/tiles/8/172/87.png new file mode 100644 index 0000000..6c77554 Binary files /dev/null and b/static/tiles/8/172/87.png differ diff --git a/static/tiles/8/172/88.png b/static/tiles/8/172/88.png new file mode 100644 index 0000000..66181a2 Binary files /dev/null and b/static/tiles/8/172/88.png differ diff --git a/static/tiles/8/172/89.png b/static/tiles/8/172/89.png new file mode 100644 index 0000000..f4c3d12 Binary files /dev/null and b/static/tiles/8/172/89.png differ diff --git a/static/tiles/8/172/90.png b/static/tiles/8/172/90.png new file mode 100644 index 0000000..c4fcb62 Binary files /dev/null and b/static/tiles/8/172/90.png differ diff --git a/static/tiles/8/172/91.png b/static/tiles/8/172/91.png new file mode 100644 index 0000000..a855c96 Binary files /dev/null and b/static/tiles/8/172/91.png differ diff --git a/static/tiles/8/172/92.png b/static/tiles/8/172/92.png new file mode 100644 index 0000000..0240a03 Binary files /dev/null and b/static/tiles/8/172/92.png differ diff --git a/static/tiles/8/172/93.png b/static/tiles/8/172/93.png new file mode 100644 index 0000000..6c38d90 Binary files /dev/null and b/static/tiles/8/172/93.png differ diff --git a/static/tiles/8/172/94.png b/static/tiles/8/172/94.png new file mode 100644 index 0000000..681e237 Binary files /dev/null and b/static/tiles/8/172/94.png differ diff --git a/static/tiles/8/172/95.png b/static/tiles/8/172/95.png new file mode 100644 index 0000000..b23bbbe Binary files /dev/null and b/static/tiles/8/172/95.png differ diff --git a/static/tiles/8/172/96.png b/static/tiles/8/172/96.png new file mode 100644 index 0000000..7af6a9c Binary files /dev/null and b/static/tiles/8/172/96.png differ diff --git a/static/tiles/8/172/97.png b/static/tiles/8/172/97.png new file mode 100644 index 0000000..4ab715a Binary files /dev/null and b/static/tiles/8/172/97.png differ diff --git a/static/tiles/8/172/98.png b/static/tiles/8/172/98.png new file mode 100644 index 0000000..251c60f Binary files /dev/null and b/static/tiles/8/172/98.png differ diff --git a/static/tiles/8/172/99.png b/static/tiles/8/172/99.png new file mode 100644 index 0000000..c1cf8cc Binary files /dev/null and b/static/tiles/8/172/99.png differ diff --git a/static/tiles/9/301/151.png b/static/tiles/9/301/151.png new file mode 100644 index 0000000..ff46246 Binary files /dev/null and b/static/tiles/9/301/151.png differ diff --git a/static/tiles/9/301/152.png b/static/tiles/9/301/152.png new file mode 100644 index 0000000..0669ce1 Binary files /dev/null and b/static/tiles/9/301/152.png differ diff --git a/static/tiles/9/301/153.png b/static/tiles/9/301/153.png new file mode 100644 index 0000000..7c6d128 Binary files /dev/null and b/static/tiles/9/301/153.png differ diff --git a/static/tiles/9/301/154.png b/static/tiles/9/301/154.png new file mode 100644 index 0000000..652c731 Binary files /dev/null and b/static/tiles/9/301/154.png differ diff --git a/static/tiles/9/301/155.png b/static/tiles/9/301/155.png new file mode 100644 index 0000000..6e95851 Binary files /dev/null and b/static/tiles/9/301/155.png differ diff --git a/static/tiles/9/301/156.png b/static/tiles/9/301/156.png new file mode 100644 index 0000000..e49a08f Binary files /dev/null and b/static/tiles/9/301/156.png differ diff --git a/static/tiles/9/301/157.png b/static/tiles/9/301/157.png new file mode 100644 index 0000000..51a0f38 Binary files /dev/null and b/static/tiles/9/301/157.png differ diff --git a/static/tiles/9/301/158.png b/static/tiles/9/301/158.png new file mode 100644 index 0000000..5307c19 Binary files /dev/null and b/static/tiles/9/301/158.png differ diff --git a/static/tiles/9/301/159.png b/static/tiles/9/301/159.png new file mode 100644 index 0000000..e0df576 Binary files /dev/null and b/static/tiles/9/301/159.png differ diff --git a/static/tiles/9/301/160.png b/static/tiles/9/301/160.png new file mode 100644 index 0000000..2e29f15 Binary files /dev/null and b/static/tiles/9/301/160.png differ diff --git a/static/tiles/9/301/161.png b/static/tiles/9/301/161.png new file mode 100644 index 0000000..458bfb3 Binary files /dev/null and b/static/tiles/9/301/161.png differ diff --git a/static/tiles/9/301/162.png b/static/tiles/9/301/162.png new file mode 100644 index 0000000..6028f67 Binary files /dev/null and b/static/tiles/9/301/162.png differ diff --git a/static/tiles/9/301/163.png b/static/tiles/9/301/163.png new file mode 100644 index 0000000..55567f5 Binary files /dev/null and b/static/tiles/9/301/163.png differ diff --git a/static/tiles/9/301/164.png b/static/tiles/9/301/164.png new file mode 100644 index 0000000..de9f44e Binary files /dev/null and b/static/tiles/9/301/164.png differ diff --git a/static/tiles/9/301/165.png b/static/tiles/9/301/165.png new file mode 100644 index 0000000..d818961 Binary files /dev/null and b/static/tiles/9/301/165.png differ diff --git a/static/tiles/9/301/166.png b/static/tiles/9/301/166.png new file mode 100644 index 0000000..cd16fc9 Binary files /dev/null and b/static/tiles/9/301/166.png differ diff --git a/static/tiles/9/301/167.png b/static/tiles/9/301/167.png new file mode 100644 index 0000000..16b29df Binary files /dev/null and b/static/tiles/9/301/167.png differ diff --git a/static/tiles/9/301/168.png b/static/tiles/9/301/168.png new file mode 100644 index 0000000..6deb1a6 Binary files /dev/null and b/static/tiles/9/301/168.png differ diff --git a/static/tiles/9/301/169.png b/static/tiles/9/301/169.png new file mode 100644 index 0000000..de54b58 Binary files /dev/null and b/static/tiles/9/301/169.png differ diff --git a/static/tiles/9/301/170.png b/static/tiles/9/301/170.png new file mode 100644 index 0000000..d2643fd Binary files /dev/null and b/static/tiles/9/301/170.png differ diff --git a/static/tiles/9/301/171.png b/static/tiles/9/301/171.png new file mode 100644 index 0000000..54643f1 Binary files /dev/null and b/static/tiles/9/301/171.png differ diff --git a/static/tiles/9/301/172.png b/static/tiles/9/301/172.png new file mode 100644 index 0000000..7626e53 Binary files /dev/null and b/static/tiles/9/301/172.png differ diff --git a/static/tiles/9/301/173.png b/static/tiles/9/301/173.png new file mode 100644 index 0000000..2b97419 Binary files /dev/null and b/static/tiles/9/301/173.png differ diff --git a/static/tiles/9/301/174.png b/static/tiles/9/301/174.png new file mode 100644 index 0000000..b8344ad Binary files /dev/null and b/static/tiles/9/301/174.png differ diff --git a/static/tiles/9/301/175.png b/static/tiles/9/301/175.png new file mode 100644 index 0000000..ace2718 Binary files /dev/null and b/static/tiles/9/301/175.png differ diff --git a/static/tiles/9/301/176.png b/static/tiles/9/301/176.png new file mode 100644 index 0000000..4daf13f Binary files /dev/null and b/static/tiles/9/301/176.png differ diff --git a/static/tiles/9/301/177.png b/static/tiles/9/301/177.png new file mode 100644 index 0000000..564269f Binary files /dev/null and b/static/tiles/9/301/177.png differ diff --git a/static/tiles/9/301/178.png b/static/tiles/9/301/178.png new file mode 100644 index 0000000..a6390b0 Binary files /dev/null and b/static/tiles/9/301/178.png differ diff --git a/static/tiles/9/301/179.png b/static/tiles/9/301/179.png new file mode 100644 index 0000000..f57a6fa Binary files /dev/null and b/static/tiles/9/301/179.png differ diff --git a/static/tiles/9/301/180.png b/static/tiles/9/301/180.png new file mode 100644 index 0000000..119f69b Binary files /dev/null and b/static/tiles/9/301/180.png differ diff --git a/static/tiles/9/301/181.png b/static/tiles/9/301/181.png new file mode 100644 index 0000000..86b467a Binary files /dev/null and b/static/tiles/9/301/181.png differ diff --git a/static/tiles/9/301/182.png b/static/tiles/9/301/182.png new file mode 100644 index 0000000..4f4fdcc Binary files /dev/null and b/static/tiles/9/301/182.png differ diff --git a/static/tiles/9/301/183.png b/static/tiles/9/301/183.png new file mode 100644 index 0000000..6d39ead Binary files /dev/null and b/static/tiles/9/301/183.png differ diff --git a/static/tiles/9/301/184.png b/static/tiles/9/301/184.png new file mode 100644 index 0000000..f607ae0 Binary files /dev/null and b/static/tiles/9/301/184.png differ diff --git a/static/tiles/9/301/185.png b/static/tiles/9/301/185.png new file mode 100644 index 0000000..f607ae0 Binary files /dev/null and b/static/tiles/9/301/185.png differ diff --git a/static/tiles/9/301/186.png b/static/tiles/9/301/186.png new file mode 100644 index 0000000..f3a771b Binary files /dev/null and b/static/tiles/9/301/186.png differ diff --git a/static/tiles/9/301/187.png b/static/tiles/9/301/187.png new file mode 100644 index 0000000..6a2ab5a Binary files /dev/null and b/static/tiles/9/301/187.png differ diff --git a/static/tiles/9/301/188.png b/static/tiles/9/301/188.png new file mode 100644 index 0000000..d6e81b0 Binary files /dev/null and b/static/tiles/9/301/188.png differ diff --git a/static/tiles/9/301/189.png b/static/tiles/9/301/189.png new file mode 100644 index 0000000..2b479c9 Binary files /dev/null and b/static/tiles/9/301/189.png differ diff --git a/static/tiles/9/301/190.png b/static/tiles/9/301/190.png new file mode 100644 index 0000000..005a50a Binary files /dev/null and b/static/tiles/9/301/190.png differ diff --git a/static/tiles/9/301/191.png b/static/tiles/9/301/191.png new file mode 100644 index 0000000..e6b4a1e Binary files /dev/null and b/static/tiles/9/301/191.png differ diff --git a/static/tiles/9/301/192.png b/static/tiles/9/301/192.png new file mode 100644 index 0000000..1d82c81 Binary files /dev/null and b/static/tiles/9/301/192.png differ diff --git a/static/tiles/9/301/193.png b/static/tiles/9/301/193.png new file mode 100644 index 0000000..0aedf52 Binary files /dev/null and b/static/tiles/9/301/193.png differ diff --git a/static/tiles/9/301/194.png b/static/tiles/9/301/194.png new file mode 100644 index 0000000..734c8a9 Binary files /dev/null and b/static/tiles/9/301/194.png differ diff --git a/static/tiles/9/301/195.png b/static/tiles/9/301/195.png new file mode 100644 index 0000000..4cc1697 Binary files /dev/null and b/static/tiles/9/301/195.png differ diff --git a/static/tiles/9/301/196.png b/static/tiles/9/301/196.png new file mode 100644 index 0000000..77c5753 Binary files /dev/null and b/static/tiles/9/301/196.png differ diff --git a/static/tiles/9/301/197.png b/static/tiles/9/301/197.png new file mode 100644 index 0000000..3649058 Binary files /dev/null and b/static/tiles/9/301/197.png differ diff --git a/static/tiles/9/301/198.png b/static/tiles/9/301/198.png new file mode 100644 index 0000000..cd4034d Binary files /dev/null and b/static/tiles/9/301/198.png differ diff --git a/static/tiles/9/301/199.png b/static/tiles/9/301/199.png new file mode 100644 index 0000000..2c249b1 Binary files /dev/null and b/static/tiles/9/301/199.png differ diff --git a/static/tiles/9/301/200.png b/static/tiles/9/301/200.png new file mode 100644 index 0000000..99199cd Binary files /dev/null and b/static/tiles/9/301/200.png differ diff --git a/static/tiles/9/301/201.png b/static/tiles/9/301/201.png new file mode 100644 index 0000000..adbf092 Binary files /dev/null and b/static/tiles/9/301/201.png differ diff --git a/static/tiles/9/301/202.png b/static/tiles/9/301/202.png new file mode 100644 index 0000000..1ff30a8 Binary files /dev/null and b/static/tiles/9/301/202.png differ diff --git a/static/tiles/9/301/203.png b/static/tiles/9/301/203.png new file mode 100644 index 0000000..da5bf05 Binary files /dev/null and b/static/tiles/9/301/203.png differ diff --git a/static/tiles/9/301/204.png b/static/tiles/9/301/204.png new file mode 100644 index 0000000..f607ae0 Binary files /dev/null and b/static/tiles/9/301/204.png differ diff --git a/static/tiles/9/301/205.png b/static/tiles/9/301/205.png new file mode 100644 index 0000000..f607ae0 Binary files /dev/null and b/static/tiles/9/301/205.png differ diff --git a/static/tiles/9/301/206.png b/static/tiles/9/301/206.png new file mode 100644 index 0000000..f607ae0 Binary files /dev/null and b/static/tiles/9/301/206.png differ diff --git a/static/tiles/9/301/207.png b/static/tiles/9/301/207.png new file mode 100644 index 0000000..d3b0c4a Binary files /dev/null and b/static/tiles/9/301/207.png differ diff --git a/static/tiles/9/301/208.png b/static/tiles/9/301/208.png new file mode 100644 index 0000000..9350848 Binary files /dev/null and b/static/tiles/9/301/208.png differ diff --git a/static/tiles/9/301/209.png b/static/tiles/9/301/209.png new file mode 100644 index 0000000..690021c Binary files /dev/null and b/static/tiles/9/301/209.png differ diff --git a/static/tiles/9/301/210.png b/static/tiles/9/301/210.png new file mode 100644 index 0000000..099c9f8 Binary files /dev/null and b/static/tiles/9/301/210.png differ diff --git a/static/tiles/9/301/211.png b/static/tiles/9/301/211.png new file mode 100644 index 0000000..0f10a8e Binary files /dev/null and b/static/tiles/9/301/211.png differ diff --git a/static/tiles/9/301/212.png b/static/tiles/9/301/212.png new file mode 100644 index 0000000..f2d978b Binary files /dev/null and b/static/tiles/9/301/212.png differ diff --git a/static/tiles/9/301/213.png b/static/tiles/9/301/213.png new file mode 100644 index 0000000..c6de639 Binary files /dev/null and b/static/tiles/9/301/213.png differ diff --git a/static/tiles/9/302/151.png b/static/tiles/9/302/151.png new file mode 100644 index 0000000..0df51ed Binary files /dev/null and b/static/tiles/9/302/151.png differ diff --git a/static/tiles/9/302/152.png b/static/tiles/9/302/152.png new file mode 100644 index 0000000..e2dffe5 Binary files /dev/null and b/static/tiles/9/302/152.png differ diff --git a/static/tiles/9/302/153.png b/static/tiles/9/302/153.png new file mode 100644 index 0000000..b29dffa Binary files /dev/null and b/static/tiles/9/302/153.png differ diff --git a/static/tiles/9/302/154.png b/static/tiles/9/302/154.png new file mode 100644 index 0000000..a78e06a Binary files /dev/null and b/static/tiles/9/302/154.png differ diff --git a/static/tiles/9/302/155.png b/static/tiles/9/302/155.png new file mode 100644 index 0000000..3c9da57 Binary files /dev/null and b/static/tiles/9/302/155.png differ diff --git a/static/tiles/9/302/156.png b/static/tiles/9/302/156.png new file mode 100644 index 0000000..f150031 Binary files /dev/null and b/static/tiles/9/302/156.png differ diff --git a/static/tiles/9/302/157.png b/static/tiles/9/302/157.png new file mode 100644 index 0000000..c9c1c7c Binary files /dev/null and b/static/tiles/9/302/157.png differ diff --git a/static/tiles/9/302/158.png b/static/tiles/9/302/158.png new file mode 100644 index 0000000..6657c10 Binary files /dev/null and b/static/tiles/9/302/158.png differ diff --git a/static/tiles/9/302/159.png b/static/tiles/9/302/159.png new file mode 100644 index 0000000..87398d3 Binary files /dev/null and b/static/tiles/9/302/159.png differ diff --git a/static/tiles/9/302/160.png b/static/tiles/9/302/160.png new file mode 100644 index 0000000..d831d41 Binary files /dev/null and b/static/tiles/9/302/160.png differ diff --git a/static/tiles/9/302/161.png b/static/tiles/9/302/161.png new file mode 100644 index 0000000..8954c0b Binary files /dev/null and b/static/tiles/9/302/161.png differ diff --git a/static/tiles/9/302/162.png b/static/tiles/9/302/162.png new file mode 100644 index 0000000..e373cf5 Binary files /dev/null and b/static/tiles/9/302/162.png differ diff --git a/static/tiles/9/302/163.png b/static/tiles/9/302/163.png new file mode 100644 index 0000000..bae03a6 Binary files /dev/null and b/static/tiles/9/302/163.png differ diff --git a/static/tiles/9/302/164.png b/static/tiles/9/302/164.png new file mode 100644 index 0000000..7e4b1bd Binary files /dev/null and b/static/tiles/9/302/164.png differ diff --git a/static/tiles/9/302/165.png b/static/tiles/9/302/165.png new file mode 100644 index 0000000..754ccd9 Binary files /dev/null and b/static/tiles/9/302/165.png differ diff --git a/static/tiles/9/302/166.png b/static/tiles/9/302/166.png new file mode 100644 index 0000000..2115b96 Binary files /dev/null and b/static/tiles/9/302/166.png differ diff --git a/static/tiles/9/302/167.png b/static/tiles/9/302/167.png new file mode 100644 index 0000000..a133654 Binary files /dev/null and b/static/tiles/9/302/167.png differ diff --git a/static/tiles/9/302/168.png b/static/tiles/9/302/168.png new file mode 100644 index 0000000..bdef89b Binary files /dev/null and b/static/tiles/9/302/168.png differ diff --git a/static/tiles/9/302/169.png b/static/tiles/9/302/169.png new file mode 100644 index 0000000..2b14ef5 Binary files /dev/null and b/static/tiles/9/302/169.png differ diff --git a/static/tiles/9/302/170.png b/static/tiles/9/302/170.png new file mode 100644 index 0000000..6e26cba Binary files /dev/null and b/static/tiles/9/302/170.png differ diff --git a/static/tiles/9/302/171.png b/static/tiles/9/302/171.png new file mode 100644 index 0000000..bcebb26 Binary files /dev/null and b/static/tiles/9/302/171.png differ diff --git a/static/tiles/9/302/172.png b/static/tiles/9/302/172.png new file mode 100644 index 0000000..04af2a1 Binary files /dev/null and b/static/tiles/9/302/172.png differ diff --git a/static/tiles/9/302/173.png b/static/tiles/9/302/173.png new file mode 100644 index 0000000..4a70e26 Binary files /dev/null and b/static/tiles/9/302/173.png differ diff --git a/static/tiles/9/302/174.png b/static/tiles/9/302/174.png new file mode 100644 index 0000000..50e67fe Binary files /dev/null and b/static/tiles/9/302/174.png differ diff --git a/static/tiles/9/302/175.png b/static/tiles/9/302/175.png new file mode 100644 index 0000000..70cce46 Binary files /dev/null and b/static/tiles/9/302/175.png differ diff --git a/static/tiles/9/302/176.png b/static/tiles/9/302/176.png new file mode 100644 index 0000000..6e3b051 Binary files /dev/null and b/static/tiles/9/302/176.png differ diff --git a/static/tiles/9/302/177.png b/static/tiles/9/302/177.png new file mode 100644 index 0000000..572e27a Binary files /dev/null and b/static/tiles/9/302/177.png differ diff --git a/static/tiles/9/302/178.png b/static/tiles/9/302/178.png new file mode 100644 index 0000000..054168e Binary files /dev/null and b/static/tiles/9/302/178.png differ diff --git a/static/tiles/9/302/179.png b/static/tiles/9/302/179.png new file mode 100644 index 0000000..04e3e70 Binary files /dev/null and b/static/tiles/9/302/179.png differ diff --git a/static/tiles/9/302/180.png b/static/tiles/9/302/180.png new file mode 100644 index 0000000..d96f861 Binary files /dev/null and b/static/tiles/9/302/180.png differ diff --git a/static/tiles/9/302/181.png b/static/tiles/9/302/181.png new file mode 100644 index 0000000..973dfee Binary files /dev/null and b/static/tiles/9/302/181.png differ diff --git a/static/tiles/9/302/182.png b/static/tiles/9/302/182.png new file mode 100644 index 0000000..7eec3ff Binary files /dev/null and b/static/tiles/9/302/182.png differ diff --git a/static/tiles/9/302/183.png b/static/tiles/9/302/183.png new file mode 100644 index 0000000..ab7fe36 Binary files /dev/null and b/static/tiles/9/302/183.png differ diff --git a/static/tiles/9/302/184.png b/static/tiles/9/302/184.png new file mode 100644 index 0000000..b48e844 Binary files /dev/null and b/static/tiles/9/302/184.png differ diff --git a/static/tiles/9/302/185.png b/static/tiles/9/302/185.png new file mode 100644 index 0000000..f607ae0 Binary files /dev/null and b/static/tiles/9/302/185.png differ diff --git a/static/tiles/9/302/186.png b/static/tiles/9/302/186.png new file mode 100644 index 0000000..bcff5cb Binary files /dev/null and b/static/tiles/9/302/186.png differ diff --git a/static/tiles/9/302/187.png b/static/tiles/9/302/187.png new file mode 100644 index 0000000..92b795c Binary files /dev/null and b/static/tiles/9/302/187.png differ diff --git a/static/tiles/9/302/188.png b/static/tiles/9/302/188.png new file mode 100644 index 0000000..1a1e699 Binary files /dev/null and b/static/tiles/9/302/188.png differ diff --git a/static/tiles/9/302/189.png b/static/tiles/9/302/189.png new file mode 100644 index 0000000..5f4b9bb Binary files /dev/null and b/static/tiles/9/302/189.png differ diff --git a/static/tiles/9/302/190.png b/static/tiles/9/302/190.png new file mode 100644 index 0000000..6b3181e Binary files /dev/null and b/static/tiles/9/302/190.png differ diff --git a/static/tiles/9/302/191.png b/static/tiles/9/302/191.png new file mode 100644 index 0000000..b026476 Binary files /dev/null and b/static/tiles/9/302/191.png differ diff --git a/static/tiles/9/302/192.png b/static/tiles/9/302/192.png new file mode 100644 index 0000000..90631f1 Binary files /dev/null and b/static/tiles/9/302/192.png differ diff --git a/static/tiles/9/302/193.png b/static/tiles/9/302/193.png new file mode 100644 index 0000000..82531e7 Binary files /dev/null and b/static/tiles/9/302/193.png differ diff --git a/static/tiles/9/302/194.png b/static/tiles/9/302/194.png new file mode 100644 index 0000000..24c6fb4 Binary files /dev/null and b/static/tiles/9/302/194.png differ diff --git a/static/tiles/9/302/195.png b/static/tiles/9/302/195.png new file mode 100644 index 0000000..aa0621c Binary files /dev/null and b/static/tiles/9/302/195.png differ diff --git a/static/tiles/9/302/196.png b/static/tiles/9/302/196.png new file mode 100644 index 0000000..0b46240 Binary files /dev/null and b/static/tiles/9/302/196.png differ diff --git a/static/tiles/9/302/197.png b/static/tiles/9/302/197.png new file mode 100644 index 0000000..a92abdf Binary files /dev/null and b/static/tiles/9/302/197.png differ diff --git a/static/tiles/9/302/198.png b/static/tiles/9/302/198.png new file mode 100644 index 0000000..13d1523 Binary files /dev/null and b/static/tiles/9/302/198.png differ diff --git a/static/tiles/9/302/199.png b/static/tiles/9/302/199.png new file mode 100644 index 0000000..94a8a8e Binary files /dev/null and b/static/tiles/9/302/199.png differ diff --git a/static/tiles/9/302/200.png b/static/tiles/9/302/200.png new file mode 100644 index 0000000..eb2d980 Binary files /dev/null and b/static/tiles/9/302/200.png differ diff --git a/static/tiles/9/302/201.png b/static/tiles/9/302/201.png new file mode 100644 index 0000000..737798b Binary files /dev/null and b/static/tiles/9/302/201.png differ diff --git a/static/tiles/9/302/202.png b/static/tiles/9/302/202.png new file mode 100644 index 0000000..fdce474 Binary files /dev/null and b/static/tiles/9/302/202.png differ diff --git a/static/tiles/9/302/203.png b/static/tiles/9/302/203.png new file mode 100644 index 0000000..f97f516 Binary files /dev/null and b/static/tiles/9/302/203.png differ diff --git a/static/tiles/9/302/204.png b/static/tiles/9/302/204.png new file mode 100644 index 0000000..f607ae0 Binary files /dev/null and b/static/tiles/9/302/204.png differ diff --git a/static/tiles/9/302/205.png b/static/tiles/9/302/205.png new file mode 100644 index 0000000..f607ae0 Binary files /dev/null and b/static/tiles/9/302/205.png differ diff --git a/static/tiles/9/302/206.png b/static/tiles/9/302/206.png new file mode 100644 index 0000000..73da713 Binary files /dev/null and b/static/tiles/9/302/206.png differ diff --git a/static/tiles/9/302/207.png b/static/tiles/9/302/207.png new file mode 100644 index 0000000..25a6a3b Binary files /dev/null and b/static/tiles/9/302/207.png differ diff --git a/static/tiles/9/302/208.png b/static/tiles/9/302/208.png new file mode 100644 index 0000000..89177a5 Binary files /dev/null and b/static/tiles/9/302/208.png differ diff --git a/static/tiles/9/302/209.png b/static/tiles/9/302/209.png new file mode 100644 index 0000000..e17c939 Binary files /dev/null and b/static/tiles/9/302/209.png differ diff --git a/static/tiles/9/302/210.png b/static/tiles/9/302/210.png new file mode 100644 index 0000000..19d6f71 Binary files /dev/null and b/static/tiles/9/302/210.png differ diff --git a/static/tiles/9/302/211.png b/static/tiles/9/302/211.png new file mode 100644 index 0000000..a3c8268 Binary files /dev/null and b/static/tiles/9/302/211.png differ diff --git a/static/tiles/9/302/212.png b/static/tiles/9/302/212.png new file mode 100644 index 0000000..969f129 Binary files /dev/null and b/static/tiles/9/302/212.png differ diff --git a/static/tiles/9/302/213.png b/static/tiles/9/302/213.png new file mode 100644 index 0000000..bcb602f Binary files /dev/null and b/static/tiles/9/302/213.png differ diff --git a/static/tiles/9/303/151.png b/static/tiles/9/303/151.png new file mode 100644 index 0000000..58baccc Binary files /dev/null and b/static/tiles/9/303/151.png differ diff --git a/static/tiles/9/303/152.png b/static/tiles/9/303/152.png new file mode 100644 index 0000000..b700a28 Binary files /dev/null and b/static/tiles/9/303/152.png differ diff --git a/static/tiles/9/303/153.png b/static/tiles/9/303/153.png new file mode 100644 index 0000000..cfafad8 Binary files /dev/null and b/static/tiles/9/303/153.png differ diff --git a/static/tiles/9/303/154.png b/static/tiles/9/303/154.png new file mode 100644 index 0000000..ca2e6b1 Binary files /dev/null and b/static/tiles/9/303/154.png differ diff --git a/static/tiles/9/303/155.png b/static/tiles/9/303/155.png new file mode 100644 index 0000000..199c5be Binary files /dev/null and b/static/tiles/9/303/155.png differ diff --git a/static/tiles/9/303/156.png b/static/tiles/9/303/156.png new file mode 100644 index 0000000..b2816a2 Binary files /dev/null and b/static/tiles/9/303/156.png differ diff --git a/static/tiles/9/303/157.png b/static/tiles/9/303/157.png new file mode 100644 index 0000000..9971383 Binary files /dev/null and b/static/tiles/9/303/157.png differ diff --git a/static/tiles/9/303/158.png b/static/tiles/9/303/158.png new file mode 100644 index 0000000..c09ec51 Binary files /dev/null and b/static/tiles/9/303/158.png differ diff --git a/static/tiles/9/303/159.png b/static/tiles/9/303/159.png new file mode 100644 index 0000000..f1ef0ca Binary files /dev/null and b/static/tiles/9/303/159.png differ diff --git a/static/tiles/9/303/160.png b/static/tiles/9/303/160.png new file mode 100644 index 0000000..4b96865 Binary files /dev/null and b/static/tiles/9/303/160.png differ diff --git a/static/tiles/9/303/161.png b/static/tiles/9/303/161.png new file mode 100644 index 0000000..4f3ef36 Binary files /dev/null and b/static/tiles/9/303/161.png differ diff --git a/static/tiles/9/303/162.png b/static/tiles/9/303/162.png new file mode 100644 index 0000000..e4c53da Binary files /dev/null and b/static/tiles/9/303/162.png differ diff --git a/static/tiles/9/303/163.png b/static/tiles/9/303/163.png new file mode 100644 index 0000000..7595f1f Binary files /dev/null and b/static/tiles/9/303/163.png differ diff --git a/static/tiles/9/303/164.png b/static/tiles/9/303/164.png new file mode 100644 index 0000000..2090326 Binary files /dev/null and b/static/tiles/9/303/164.png differ diff --git a/static/tiles/9/303/165.png b/static/tiles/9/303/165.png new file mode 100644 index 0000000..45f04a7 Binary files /dev/null and b/static/tiles/9/303/165.png differ diff --git a/static/tiles/9/303/166.png b/static/tiles/9/303/166.png new file mode 100644 index 0000000..9f1da29 Binary files /dev/null and b/static/tiles/9/303/166.png differ diff --git a/static/tiles/9/303/167.png b/static/tiles/9/303/167.png new file mode 100644 index 0000000..3d8ba07 Binary files /dev/null and b/static/tiles/9/303/167.png differ diff --git a/static/tiles/9/303/168.png b/static/tiles/9/303/168.png new file mode 100644 index 0000000..e75c32d Binary files /dev/null and b/static/tiles/9/303/168.png differ diff --git a/static/tiles/9/303/169.png b/static/tiles/9/303/169.png new file mode 100644 index 0000000..e826690 Binary files /dev/null and b/static/tiles/9/303/169.png differ diff --git a/static/tiles/9/303/170.png b/static/tiles/9/303/170.png new file mode 100644 index 0000000..684914e Binary files /dev/null and b/static/tiles/9/303/170.png differ diff --git a/static/tiles/9/303/171.png b/static/tiles/9/303/171.png new file mode 100644 index 0000000..9e7f8df Binary files /dev/null and b/static/tiles/9/303/171.png differ diff --git a/static/tiles/9/303/172.png b/static/tiles/9/303/172.png new file mode 100644 index 0000000..b714844 Binary files /dev/null and b/static/tiles/9/303/172.png differ diff --git a/static/tiles/9/303/173.png b/static/tiles/9/303/173.png new file mode 100644 index 0000000..3d2bb07 Binary files /dev/null and b/static/tiles/9/303/173.png differ diff --git a/static/tiles/9/303/174.png b/static/tiles/9/303/174.png new file mode 100644 index 0000000..68acf7e Binary files /dev/null and b/static/tiles/9/303/174.png differ diff --git a/static/tiles/9/303/175.png b/static/tiles/9/303/175.png new file mode 100644 index 0000000..2e20c44 Binary files /dev/null and b/static/tiles/9/303/175.png differ diff --git a/static/tiles/9/303/176.png b/static/tiles/9/303/176.png new file mode 100644 index 0000000..d4ccb5d Binary files /dev/null and b/static/tiles/9/303/176.png differ diff --git a/static/tiles/9/303/177.png b/static/tiles/9/303/177.png new file mode 100644 index 0000000..98c6570 Binary files /dev/null and b/static/tiles/9/303/177.png differ diff --git a/static/tiles/9/303/178.png b/static/tiles/9/303/178.png new file mode 100644 index 0000000..eae039d Binary files /dev/null and b/static/tiles/9/303/178.png differ diff --git a/static/tiles/9/303/179.png b/static/tiles/9/303/179.png new file mode 100644 index 0000000..f485c20 Binary files /dev/null and b/static/tiles/9/303/179.png differ diff --git a/static/tiles/9/303/180.png b/static/tiles/9/303/180.png new file mode 100644 index 0000000..53c26aa Binary files /dev/null and b/static/tiles/9/303/180.png differ diff --git a/static/tiles/9/303/181.png b/static/tiles/9/303/181.png new file mode 100644 index 0000000..3fb5ecf Binary files /dev/null and b/static/tiles/9/303/181.png differ diff --git a/static/tiles/9/303/182.png b/static/tiles/9/303/182.png new file mode 100644 index 0000000..aba1d6a Binary files /dev/null and b/static/tiles/9/303/182.png differ diff --git a/static/tiles/9/303/183.png b/static/tiles/9/303/183.png new file mode 100644 index 0000000..dd16b5c Binary files /dev/null and b/static/tiles/9/303/183.png differ diff --git a/static/tiles/9/303/184.png b/static/tiles/9/303/184.png new file mode 100644 index 0000000..e6f2917 Binary files /dev/null and b/static/tiles/9/303/184.png differ diff --git a/static/tiles/9/303/185.png b/static/tiles/9/303/185.png new file mode 100644 index 0000000..8438285 Binary files /dev/null and b/static/tiles/9/303/185.png differ diff --git a/static/tiles/9/303/186.png b/static/tiles/9/303/186.png new file mode 100644 index 0000000..f607ae0 Binary files /dev/null and b/static/tiles/9/303/186.png differ diff --git a/static/tiles/9/303/187.png b/static/tiles/9/303/187.png new file mode 100644 index 0000000..44a407d Binary files /dev/null and b/static/tiles/9/303/187.png differ diff --git a/static/tiles/9/303/188.png b/static/tiles/9/303/188.png new file mode 100644 index 0000000..fff902a Binary files /dev/null and b/static/tiles/9/303/188.png differ diff --git a/static/tiles/9/303/189.png b/static/tiles/9/303/189.png new file mode 100644 index 0000000..2b5afc7 Binary files /dev/null and b/static/tiles/9/303/189.png differ diff --git a/static/tiles/9/303/190.png b/static/tiles/9/303/190.png new file mode 100644 index 0000000..48fbd14 Binary files /dev/null and b/static/tiles/9/303/190.png differ diff --git a/static/tiles/9/303/191.png b/static/tiles/9/303/191.png new file mode 100644 index 0000000..f2b1a69 Binary files /dev/null and b/static/tiles/9/303/191.png differ diff --git a/static/tiles/9/303/192.png b/static/tiles/9/303/192.png new file mode 100644 index 0000000..cce3c49 Binary files /dev/null and b/static/tiles/9/303/192.png differ diff --git a/static/tiles/9/303/193.png b/static/tiles/9/303/193.png new file mode 100644 index 0000000..f29677c Binary files /dev/null and b/static/tiles/9/303/193.png differ diff --git a/static/tiles/9/303/194.png b/static/tiles/9/303/194.png new file mode 100644 index 0000000..5f232d6 Binary files /dev/null and b/static/tiles/9/303/194.png differ diff --git a/static/tiles/9/303/195.png b/static/tiles/9/303/195.png new file mode 100644 index 0000000..716afc4 Binary files /dev/null and b/static/tiles/9/303/195.png differ diff --git a/static/tiles/9/303/196.png b/static/tiles/9/303/196.png new file mode 100644 index 0000000..f22747c Binary files /dev/null and b/static/tiles/9/303/196.png differ diff --git a/static/tiles/9/303/197.png b/static/tiles/9/303/197.png new file mode 100644 index 0000000..f20cc75 Binary files /dev/null and b/static/tiles/9/303/197.png differ diff --git a/static/tiles/9/303/198.png b/static/tiles/9/303/198.png new file mode 100644 index 0000000..267b3ec Binary files /dev/null and b/static/tiles/9/303/198.png differ diff --git a/static/tiles/9/303/199.png b/static/tiles/9/303/199.png new file mode 100644 index 0000000..1df8406 Binary files /dev/null and b/static/tiles/9/303/199.png differ diff --git a/static/tiles/9/303/200.png b/static/tiles/9/303/200.png new file mode 100644 index 0000000..11f413c Binary files /dev/null and b/static/tiles/9/303/200.png differ diff --git a/static/tiles/9/303/201.png b/static/tiles/9/303/201.png new file mode 100644 index 0000000..970ce62 Binary files /dev/null and b/static/tiles/9/303/201.png differ diff --git a/static/tiles/9/303/202.png b/static/tiles/9/303/202.png new file mode 100644 index 0000000..20d6a9c Binary files /dev/null and b/static/tiles/9/303/202.png differ diff --git a/static/tiles/9/303/203.png b/static/tiles/9/303/203.png new file mode 100644 index 0000000..04898a2 Binary files /dev/null and b/static/tiles/9/303/203.png differ diff --git a/static/tiles/9/303/204.png b/static/tiles/9/303/204.png new file mode 100644 index 0000000..6a8d98d Binary files /dev/null and b/static/tiles/9/303/204.png differ diff --git a/static/tiles/9/303/205.png b/static/tiles/9/303/205.png new file mode 100644 index 0000000..4aeabfe Binary files /dev/null and b/static/tiles/9/303/205.png differ diff --git a/static/tiles/9/303/206.png b/static/tiles/9/303/206.png new file mode 100644 index 0000000..f607ae0 Binary files /dev/null and b/static/tiles/9/303/206.png differ diff --git a/static/tiles/9/303/207.png b/static/tiles/9/303/207.png new file mode 100644 index 0000000..f607ae0 Binary files /dev/null and b/static/tiles/9/303/207.png differ diff --git a/static/tiles/9/303/208.png b/static/tiles/9/303/208.png new file mode 100644 index 0000000..e6b7fff Binary files /dev/null and b/static/tiles/9/303/208.png differ diff --git a/static/tiles/9/303/209.png b/static/tiles/9/303/209.png new file mode 100644 index 0000000..7c4f9df Binary files /dev/null and b/static/tiles/9/303/209.png differ diff --git a/static/tiles/9/303/210.png b/static/tiles/9/303/210.png new file mode 100644 index 0000000..6762ca7 Binary files /dev/null and b/static/tiles/9/303/210.png differ diff --git a/static/tiles/9/303/211.png b/static/tiles/9/303/211.png new file mode 100644 index 0000000..68c07bb Binary files /dev/null and b/static/tiles/9/303/211.png differ diff --git a/static/tiles/9/303/212.png b/static/tiles/9/303/212.png new file mode 100644 index 0000000..db55275 Binary files /dev/null and b/static/tiles/9/303/212.png differ diff --git a/static/tiles/9/303/213.png b/static/tiles/9/303/213.png new file mode 100644 index 0000000..1303684 Binary files /dev/null and b/static/tiles/9/303/213.png differ diff --git a/static/tiles/9/304/151.png b/static/tiles/9/304/151.png new file mode 100644 index 0000000..f86ed44 Binary files /dev/null and b/static/tiles/9/304/151.png differ diff --git a/static/tiles/9/304/152.png b/static/tiles/9/304/152.png new file mode 100644 index 0000000..bb5dcda Binary files /dev/null and b/static/tiles/9/304/152.png differ diff --git a/static/tiles/9/304/153.png b/static/tiles/9/304/153.png new file mode 100644 index 0000000..cf519b8 Binary files /dev/null and b/static/tiles/9/304/153.png differ diff --git a/static/tiles/9/304/154.png b/static/tiles/9/304/154.png new file mode 100644 index 0000000..a6b551d Binary files /dev/null and b/static/tiles/9/304/154.png differ diff --git a/static/tiles/9/304/155.png b/static/tiles/9/304/155.png new file mode 100644 index 0000000..cb0279f Binary files /dev/null and b/static/tiles/9/304/155.png differ diff --git a/static/tiles/9/304/156.png b/static/tiles/9/304/156.png new file mode 100644 index 0000000..9dd1dc8 Binary files /dev/null and b/static/tiles/9/304/156.png differ diff --git a/static/tiles/9/304/157.png b/static/tiles/9/304/157.png new file mode 100644 index 0000000..8f7c51a Binary files /dev/null and b/static/tiles/9/304/157.png differ diff --git a/static/tiles/9/304/158.png b/static/tiles/9/304/158.png new file mode 100644 index 0000000..984c590 Binary files /dev/null and b/static/tiles/9/304/158.png differ diff --git a/static/tiles/9/304/159.png b/static/tiles/9/304/159.png new file mode 100644 index 0000000..384f09e Binary files /dev/null and b/static/tiles/9/304/159.png differ diff --git a/static/tiles/9/304/160.png b/static/tiles/9/304/160.png new file mode 100644 index 0000000..4107ede Binary files /dev/null and b/static/tiles/9/304/160.png differ diff --git a/static/tiles/9/304/161.png b/static/tiles/9/304/161.png new file mode 100644 index 0000000..accd9b9 Binary files /dev/null and b/static/tiles/9/304/161.png differ diff --git a/static/tiles/9/304/162.png b/static/tiles/9/304/162.png new file mode 100644 index 0000000..0fadd5e Binary files /dev/null and b/static/tiles/9/304/162.png differ diff --git a/static/tiles/9/304/163.png b/static/tiles/9/304/163.png new file mode 100644 index 0000000..b852d48 Binary files /dev/null and b/static/tiles/9/304/163.png differ diff --git a/static/tiles/9/304/164.png b/static/tiles/9/304/164.png new file mode 100644 index 0000000..2e2358e Binary files /dev/null and b/static/tiles/9/304/164.png differ diff --git a/static/tiles/9/304/165.png b/static/tiles/9/304/165.png new file mode 100644 index 0000000..f33f720 Binary files /dev/null and b/static/tiles/9/304/165.png differ diff --git a/static/tiles/9/304/166.png b/static/tiles/9/304/166.png new file mode 100644 index 0000000..f98cdd8 Binary files /dev/null and b/static/tiles/9/304/166.png differ diff --git a/static/tiles/9/304/167.png b/static/tiles/9/304/167.png new file mode 100644 index 0000000..25b2bcf Binary files /dev/null and b/static/tiles/9/304/167.png differ diff --git a/static/tiles/9/304/168.png b/static/tiles/9/304/168.png new file mode 100644 index 0000000..040690d Binary files /dev/null and b/static/tiles/9/304/168.png differ diff --git a/static/tiles/9/304/169.png b/static/tiles/9/304/169.png new file mode 100644 index 0000000..46b60ac Binary files /dev/null and b/static/tiles/9/304/169.png differ diff --git a/static/tiles/9/304/170.png b/static/tiles/9/304/170.png new file mode 100644 index 0000000..f8ce824 Binary files /dev/null and b/static/tiles/9/304/170.png differ diff --git a/static/tiles/9/304/171.png b/static/tiles/9/304/171.png new file mode 100644 index 0000000..90d3f7f Binary files /dev/null and b/static/tiles/9/304/171.png differ diff --git a/static/tiles/9/304/172.png b/static/tiles/9/304/172.png new file mode 100644 index 0000000..ee461a0 Binary files /dev/null and b/static/tiles/9/304/172.png differ diff --git a/static/tiles/9/304/173.png b/static/tiles/9/304/173.png new file mode 100644 index 0000000..2cf9df1 Binary files /dev/null and b/static/tiles/9/304/173.png differ diff --git a/static/tiles/9/304/174.png b/static/tiles/9/304/174.png new file mode 100644 index 0000000..888c16d Binary files /dev/null and b/static/tiles/9/304/174.png differ diff --git a/static/tiles/9/304/175.png b/static/tiles/9/304/175.png new file mode 100644 index 0000000..e98a3ad Binary files /dev/null and b/static/tiles/9/304/175.png differ diff --git a/static/tiles/9/304/176.png b/static/tiles/9/304/176.png new file mode 100644 index 0000000..fc69fd2 Binary files /dev/null and b/static/tiles/9/304/176.png differ diff --git a/static/tiles/9/304/177.png b/static/tiles/9/304/177.png new file mode 100644 index 0000000..243986f Binary files /dev/null and b/static/tiles/9/304/177.png differ diff --git a/static/tiles/9/304/178.png b/static/tiles/9/304/178.png new file mode 100644 index 0000000..f4c1d7f Binary files /dev/null and b/static/tiles/9/304/178.png differ diff --git a/static/tiles/9/304/179.png b/static/tiles/9/304/179.png new file mode 100644 index 0000000..b7f18b1 Binary files /dev/null and b/static/tiles/9/304/179.png differ diff --git a/static/tiles/9/304/180.png b/static/tiles/9/304/180.png new file mode 100644 index 0000000..ad1f527 Binary files /dev/null and b/static/tiles/9/304/180.png differ diff --git a/static/tiles/9/304/181.png b/static/tiles/9/304/181.png new file mode 100644 index 0000000..d487265 Binary files /dev/null and b/static/tiles/9/304/181.png differ diff --git a/static/tiles/9/304/182.png b/static/tiles/9/304/182.png new file mode 100644 index 0000000..8b405db Binary files /dev/null and b/static/tiles/9/304/182.png differ diff --git a/static/tiles/9/304/183.png b/static/tiles/9/304/183.png new file mode 100644 index 0000000..7b0d120 Binary files /dev/null and b/static/tiles/9/304/183.png differ diff --git a/static/tiles/9/304/184.png b/static/tiles/9/304/184.png new file mode 100644 index 0000000..394447c Binary files /dev/null and b/static/tiles/9/304/184.png differ diff --git a/static/tiles/9/304/185.png b/static/tiles/9/304/185.png new file mode 100644 index 0000000..31dc8ff Binary files /dev/null and b/static/tiles/9/304/185.png differ diff --git a/static/tiles/9/304/186.png b/static/tiles/9/304/186.png new file mode 100644 index 0000000..f607ae0 Binary files /dev/null and b/static/tiles/9/304/186.png differ diff --git a/static/tiles/9/304/187.png b/static/tiles/9/304/187.png new file mode 100644 index 0000000..e83db42 Binary files /dev/null and b/static/tiles/9/304/187.png differ diff --git a/static/tiles/9/304/188.png b/static/tiles/9/304/188.png new file mode 100644 index 0000000..6c32831 Binary files /dev/null and b/static/tiles/9/304/188.png differ diff --git a/static/tiles/9/304/189.png b/static/tiles/9/304/189.png new file mode 100644 index 0000000..213fa60 Binary files /dev/null and b/static/tiles/9/304/189.png differ diff --git a/static/tiles/9/304/190.png b/static/tiles/9/304/190.png new file mode 100644 index 0000000..db28c7c Binary files /dev/null and b/static/tiles/9/304/190.png differ diff --git a/static/tiles/9/304/191.png b/static/tiles/9/304/191.png new file mode 100644 index 0000000..c13f178 Binary files /dev/null and b/static/tiles/9/304/191.png differ diff --git a/static/tiles/9/309/159.png b/static/tiles/9/309/159.png new file mode 100644 index 0000000..792bf6f Binary files /dev/null and b/static/tiles/9/309/159.png differ diff --git a/static/tiles/9/309/160.png b/static/tiles/9/309/160.png new file mode 100644 index 0000000..c82cdb6 Binary files /dev/null and b/static/tiles/9/309/160.png differ diff --git a/static/xterm/xterm-addon-fit.min.js b/static/xterm/xterm-addon-fit.min.js new file mode 100644 index 0000000..850c473 --- /dev/null +++ b/static/xterm/xterm-addon-fit.min.js @@ -0,0 +1,7 @@ +/** + * Skipped minification because the original files appears to be already minified. + * Original file: /npm/xterm-addon-fit@0.8.0/lib/xterm-addon-fit.js + * + * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files + */ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.FitAddon=t():e.FitAddon=t()}(self,(()=>(()=>{"use strict";var e={};return(()=>{var t=e;Object.defineProperty(t,"__esModule",{value:!0}),t.FitAddon=void 0,t.FitAddon=class{activate(e){this._terminal=e}dispose(){}fit(){const e=this.proposeDimensions();if(!e||!this._terminal||isNaN(e.cols)||isNaN(e.rows))return;const t=this._terminal._core;this._terminal.rows===e.rows&&this._terminal.cols===e.cols||(t._renderService.clear(),this._terminal.resize(e.cols,e.rows))}proposeDimensions(){if(!this._terminal)return;if(!this._terminal.element||!this._terminal.element.parentElement)return;const e=this._terminal._core,t=e._renderService.dimensions;if(0===t.css.cell.width||0===t.css.cell.height)return;const r=0===this._terminal.options.scrollback?0:e.viewport.scrollBarWidth,i=window.getComputedStyle(this._terminal.element.parentElement),o=parseInt(i.getPropertyValue("height")),s=Math.max(0,parseInt(i.getPropertyValue("width"))),n=window.getComputedStyle(this._terminal.element),l=o-(parseInt(n.getPropertyValue("padding-top"))+parseInt(n.getPropertyValue("padding-bottom"))),a=s-(parseInt(n.getPropertyValue("padding-right"))+parseInt(n.getPropertyValue("padding-left")))-r;return{cols:Math.max(2,Math.floor(a/t.css.cell.width)),rows:Math.max(1,Math.floor(l/t.css.cell.height))}}}})(),e})())); \ No newline at end of file diff --git a/static/xterm/xterm.css b/static/xterm/xterm.css new file mode 100644 index 0000000..74acc26 --- /dev/null +++ b/static/xterm/xterm.css @@ -0,0 +1,209 @@ +/** + * Copyright (c) 2014 The xterm.js authors. All rights reserved. + * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License) + * https://github.com/chjj/term.js + * @license MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * Originally forked from (with the author's permission): + * Fabrice Bellard's javascript vt100 for jslinux: + * http://bellard.org/jslinux/ + * Copyright (c) 2011 Fabrice Bellard + * The original design remains. The terminal itself + * has been extended to include xterm CSI codes, among + * other features. + */ + +/** + * Default styles for xterm.js + */ + +.xterm { + cursor: text; + position: relative; + user-select: none; + -ms-user-select: none; + -webkit-user-select: none; +} + +.xterm.focus, +.xterm:focus { + outline: none; +} + +.xterm .xterm-helpers { + position: absolute; + top: 0; + /** + * The z-index of the helpers must be higher than the canvases in order for + * IMEs to appear on top. + */ + z-index: 5; +} + +.xterm .xterm-helper-textarea { + padding: 0; + border: 0; + margin: 0; + /* Move textarea out of the screen to the far left, so that the cursor is not visible */ + position: absolute; + opacity: 0; + left: -9999em; + top: 0; + width: 0; + height: 0; + z-index: -5; + /** Prevent wrapping so the IME appears against the textarea at the correct position */ + white-space: nowrap; + overflow: hidden; + resize: none; +} + +.xterm .composition-view { + /* TODO: Composition position got messed up somewhere */ + background: #000; + color: #FFF; + display: none; + position: absolute; + white-space: nowrap; + z-index: 1; +} + +.xterm .composition-view.active { + display: block; +} + +.xterm .xterm-viewport { + /* On OS X this is required in order for the scroll bar to appear fully opaque */ + background-color: #000; + overflow-y: scroll; + cursor: default; + position: absolute; + right: 0; + left: 0; + top: 0; + bottom: 0; +} + +.xterm .xterm-screen { + position: relative; +} + +.xterm .xterm-screen canvas { + position: absolute; + left: 0; + top: 0; +} + +.xterm .xterm-scroll-area { + visibility: hidden; +} + +.xterm-char-measure-element { + display: inline-block; + visibility: hidden; + position: absolute; + top: 0; + left: -9999em; + line-height: normal; +} + +.xterm.enable-mouse-events { + /* When mouse events are enabled (eg. tmux), revert to the standard pointer cursor */ + cursor: default; +} + +.xterm.xterm-cursor-pointer, +.xterm .xterm-cursor-pointer { + cursor: pointer; +} + +.xterm.column-select.focus { + /* Column selection mode */ + cursor: crosshair; +} + +.xterm .xterm-accessibility, +.xterm .xterm-message { + position: absolute; + left: 0; + top: 0; + bottom: 0; + right: 0; + z-index: 10; + color: transparent; + pointer-events: none; +} + +.xterm .live-region { + position: absolute; + left: -9999px; + width: 1px; + height: 1px; + overflow: hidden; +} + +.xterm-dim { + /* Dim should not apply to background, so the opacity of the foreground color is applied + * explicitly in the generated class and reset to 1 here */ + opacity: 1 !important; +} + +.xterm-underline-1 { text-decoration: underline; } +.xterm-underline-2 { text-decoration: double underline; } +.xterm-underline-3 { text-decoration: wavy underline; } +.xterm-underline-4 { text-decoration: dotted underline; } +.xterm-underline-5 { text-decoration: dashed underline; } + +.xterm-overline { + text-decoration: overline; +} + +.xterm-overline.xterm-underline-1 { text-decoration: overline underline; } +.xterm-overline.xterm-underline-2 { text-decoration: overline double underline; } +.xterm-overline.xterm-underline-3 { text-decoration: overline wavy underline; } +.xterm-overline.xterm-underline-4 { text-decoration: overline dotted underline; } +.xterm-overline.xterm-underline-5 { text-decoration: overline dashed underline; } + +.xterm-strikethrough { + text-decoration: line-through; +} + +.xterm-screen .xterm-decoration-container .xterm-decoration { + z-index: 6; + position: absolute; +} + +.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer { + z-index: 7; +} + +.xterm-decoration-overview-ruler { + z-index: 8; + position: absolute; + top: 0; + right: 0; + pointer-events: none; +} + +.xterm-decoration-top { + z-index: 2; + position: relative; +} diff --git a/static/xterm/xterm.min.js b/static/xterm/xterm.min.js new file mode 100644 index 0000000..f3517f2 --- /dev/null +++ b/static/xterm/xterm.min.js @@ -0,0 +1,7 @@ +/** + * Skipped minification because the original files appears to be already minified. + * Original file: /npm/xterm@5.3.0/lib/xterm.js + * + * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files + */ +!function(e,t){if("object"==typeof exports&&"object"==typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var i=t();for(var s in i)("object"==typeof exports?exports:e)[s]=i[s]}}(self,(()=>(()=>{"use strict";var e={4567:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.AccessibilityManager=void 0;const n=i(9042),o=i(6114),a=i(9924),h=i(844),c=i(5596),l=i(4725),d=i(3656);let _=t.AccessibilityManager=class extends h.Disposable{constructor(e,t){super(),this._terminal=e,this._renderService=t,this._liveRegionLineCount=0,this._charsToConsume=[],this._charsToAnnounce="",this._accessibilityContainer=document.createElement("div"),this._accessibilityContainer.classList.add("xterm-accessibility"),this._rowContainer=document.createElement("div"),this._rowContainer.setAttribute("role","list"),this._rowContainer.classList.add("xterm-accessibility-tree"),this._rowElements=[];for(let e=0;ethis._handleBoundaryFocus(e,0),this._bottomBoundaryFocusListener=e=>this._handleBoundaryFocus(e,1),this._rowElements[0].addEventListener("focus",this._topBoundaryFocusListener),this._rowElements[this._rowElements.length-1].addEventListener("focus",this._bottomBoundaryFocusListener),this._refreshRowsDimensions(),this._accessibilityContainer.appendChild(this._rowContainer),this._liveRegion=document.createElement("div"),this._liveRegion.classList.add("live-region"),this._liveRegion.setAttribute("aria-live","assertive"),this._accessibilityContainer.appendChild(this._liveRegion),this._liveRegionDebouncer=this.register(new a.TimeBasedDebouncer(this._renderRows.bind(this))),!this._terminal.element)throw new Error("Cannot enable accessibility before Terminal.open");this._terminal.element.insertAdjacentElement("afterbegin",this._accessibilityContainer),this.register(this._terminal.onResize((e=>this._handleResize(e.rows)))),this.register(this._terminal.onRender((e=>this._refreshRows(e.start,e.end)))),this.register(this._terminal.onScroll((()=>this._refreshRows()))),this.register(this._terminal.onA11yChar((e=>this._handleChar(e)))),this.register(this._terminal.onLineFeed((()=>this._handleChar("\n")))),this.register(this._terminal.onA11yTab((e=>this._handleTab(e)))),this.register(this._terminal.onKey((e=>this._handleKey(e.key)))),this.register(this._terminal.onBlur((()=>this._clearLiveRegion()))),this.register(this._renderService.onDimensionsChange((()=>this._refreshRowsDimensions()))),this._screenDprMonitor=new c.ScreenDprMonitor(window),this.register(this._screenDprMonitor),this._screenDprMonitor.setListener((()=>this._refreshRowsDimensions())),this.register((0,d.addDisposableDomListener)(window,"resize",(()=>this._refreshRowsDimensions()))),this._refreshRows(),this.register((0,h.toDisposable)((()=>{this._accessibilityContainer.remove(),this._rowElements.length=0})))}_handleTab(e){for(let t=0;t0?this._charsToConsume.shift()!==e&&(this._charsToAnnounce+=e):this._charsToAnnounce+=e,"\n"===e&&(this._liveRegionLineCount++,21===this._liveRegionLineCount&&(this._liveRegion.textContent+=n.tooMuchOutput)),o.isMac&&this._liveRegion.textContent&&this._liveRegion.textContent.length>0&&!this._liveRegion.parentNode&&setTimeout((()=>{this._accessibilityContainer.appendChild(this._liveRegion)}),0))}_clearLiveRegion(){this._liveRegion.textContent="",this._liveRegionLineCount=0,o.isMac&&this._liveRegion.remove()}_handleKey(e){this._clearLiveRegion(),/\p{Control}/u.test(e)||this._charsToConsume.push(e)}_refreshRows(e,t){this._liveRegionDebouncer.refresh(e,t,this._terminal.rows)}_renderRows(e,t){const i=this._terminal.buffer,s=i.lines.length.toString();for(let r=e;r<=t;r++){const e=i.translateBufferLineToString(i.ydisp+r,!0),t=(i.ydisp+r+1).toString(),n=this._rowElements[r];n&&(0===e.length?n.innerText=" ":n.textContent=e,n.setAttribute("aria-posinset",t),n.setAttribute("aria-setsize",s))}this._announceCharacters()}_announceCharacters(){0!==this._charsToAnnounce.length&&(this._liveRegion.textContent+=this._charsToAnnounce,this._charsToAnnounce="")}_handleBoundaryFocus(e,t){const i=e.target,s=this._rowElements[0===t?1:this._rowElements.length-2];if(i.getAttribute("aria-posinset")===(0===t?"1":`${this._terminal.buffer.lines.length}`))return;if(e.relatedTarget!==s)return;let r,n;if(0===t?(r=i,n=this._rowElements.pop(),this._rowContainer.removeChild(n)):(r=this._rowElements.shift(),n=i,this._rowContainer.removeChild(r)),r.removeEventListener("focus",this._topBoundaryFocusListener),n.removeEventListener("focus",this._bottomBoundaryFocusListener),0===t){const e=this._createAccessibilityTreeNode();this._rowElements.unshift(e),this._rowContainer.insertAdjacentElement("afterbegin",e)}else{const e=this._createAccessibilityTreeNode();this._rowElements.push(e),this._rowContainer.appendChild(e)}this._rowElements[0].addEventListener("focus",this._topBoundaryFocusListener),this._rowElements[this._rowElements.length-1].addEventListener("focus",this._bottomBoundaryFocusListener),this._terminal.scrollLines(0===t?-1:1),this._rowElements[0===t?1:this._rowElements.length-2].focus(),e.preventDefault(),e.stopImmediatePropagation()}_handleResize(e){this._rowElements[this._rowElements.length-1].removeEventListener("focus",this._bottomBoundaryFocusListener);for(let e=this._rowContainer.children.length;ee;)this._rowContainer.removeChild(this._rowElements.pop());this._rowElements[this._rowElements.length-1].addEventListener("focus",this._bottomBoundaryFocusListener),this._refreshRowsDimensions()}_createAccessibilityTreeNode(){const e=document.createElement("div");return e.setAttribute("role","listitem"),e.tabIndex=-1,this._refreshRowDimensions(e),e}_refreshRowsDimensions(){if(this._renderService.dimensions.css.cell.height){this._accessibilityContainer.style.width=`${this._renderService.dimensions.css.canvas.width}px`,this._rowElements.length!==this._terminal.rows&&this._handleResize(this._terminal.rows);for(let e=0;e{function i(e){return e.replace(/\r?\n/g,"\r")}function s(e,t){return t?"[200~"+e+"[201~":e}function r(e,t,r,n){e=s(e=i(e),r.decPrivateModes.bracketedPasteMode&&!0!==n.rawOptions.ignoreBracketedPasteMode),r.triggerDataEvent(e,!0),t.value=""}function n(e,t,i){const s=i.getBoundingClientRect(),r=e.clientX-s.left-10,n=e.clientY-s.top-10;t.style.width="20px",t.style.height="20px",t.style.left=`${r}px`,t.style.top=`${n}px`,t.style.zIndex="1000",t.focus()}Object.defineProperty(t,"__esModule",{value:!0}),t.rightClickHandler=t.moveTextAreaUnderMouseCursor=t.paste=t.handlePasteEvent=t.copyHandler=t.bracketTextForPaste=t.prepareTextForTerminal=void 0,t.prepareTextForTerminal=i,t.bracketTextForPaste=s,t.copyHandler=function(e,t){e.clipboardData&&e.clipboardData.setData("text/plain",t.selectionText),e.preventDefault()},t.handlePasteEvent=function(e,t,i,s){e.stopPropagation(),e.clipboardData&&r(e.clipboardData.getData("text/plain"),t,i,s)},t.paste=r,t.moveTextAreaUnderMouseCursor=n,t.rightClickHandler=function(e,t,i,s,r){n(e,t,i),r&&s.rightClickSelect(e),t.value=s.selectionText,t.select()}},7239:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.ColorContrastCache=void 0;const s=i(1505);t.ColorContrastCache=class{constructor(){this._color=new s.TwoKeyMap,this._css=new s.TwoKeyMap}setCss(e,t,i){this._css.set(e,t,i)}getCss(e,t){return this._css.get(e,t)}setColor(e,t,i){this._color.set(e,t,i)}getColor(e,t){return this._color.get(e,t)}clear(){this._color.clear(),this._css.clear()}}},3656:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.addDisposableDomListener=void 0,t.addDisposableDomListener=function(e,t,i,s){e.addEventListener(t,i,s);let r=!1;return{dispose:()=>{r||(r=!0,e.removeEventListener(t,i,s))}}}},6465:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.Linkifier2=void 0;const n=i(3656),o=i(8460),a=i(844),h=i(2585);let c=t.Linkifier2=class extends a.Disposable{get currentLink(){return this._currentLink}constructor(e){super(),this._bufferService=e,this._linkProviders=[],this._linkCacheDisposables=[],this._isMouseOut=!0,this._wasResized=!1,this._activeLine=-1,this._onShowLinkUnderline=this.register(new o.EventEmitter),this.onShowLinkUnderline=this._onShowLinkUnderline.event,this._onHideLinkUnderline=this.register(new o.EventEmitter),this.onHideLinkUnderline=this._onHideLinkUnderline.event,this.register((0,a.getDisposeArrayDisposable)(this._linkCacheDisposables)),this.register((0,a.toDisposable)((()=>{this._lastMouseEvent=void 0}))),this.register(this._bufferService.onResize((()=>{this._clearCurrentLink(),this._wasResized=!0})))}registerLinkProvider(e){return this._linkProviders.push(e),{dispose:()=>{const t=this._linkProviders.indexOf(e);-1!==t&&this._linkProviders.splice(t,1)}}}attachToDom(e,t,i){this._element=e,this._mouseService=t,this._renderService=i,this.register((0,n.addDisposableDomListener)(this._element,"mouseleave",(()=>{this._isMouseOut=!0,this._clearCurrentLink()}))),this.register((0,n.addDisposableDomListener)(this._element,"mousemove",this._handleMouseMove.bind(this))),this.register((0,n.addDisposableDomListener)(this._element,"mousedown",this._handleMouseDown.bind(this))),this.register((0,n.addDisposableDomListener)(this._element,"mouseup",this._handleMouseUp.bind(this)))}_handleMouseMove(e){if(this._lastMouseEvent=e,!this._element||!this._mouseService)return;const t=this._positionFromMouseEvent(e,this._element,this._mouseService);if(!t)return;this._isMouseOut=!1;const i=e.composedPath();for(let e=0;e{null==e||e.forEach((e=>{e.link.dispose&&e.link.dispose()}))})),this._activeProviderReplies=new Map,this._activeLine=e.y);let r=!1;for(const[i,n]of this._linkProviders.entries())t?(null===(s=this._activeProviderReplies)||void 0===s?void 0:s.get(i))&&(r=this._checkLinkProviderResult(i,e,r)):n.provideLinks(e.y,(t=>{var s,n;if(this._isMouseOut)return;const o=null==t?void 0:t.map((e=>({link:e})));null===(s=this._activeProviderReplies)||void 0===s||s.set(i,o),r=this._checkLinkProviderResult(i,e,r),(null===(n=this._activeProviderReplies)||void 0===n?void 0:n.size)===this._linkProviders.length&&this._removeIntersectingLinks(e.y,this._activeProviderReplies)}))}_removeIntersectingLinks(e,t){const i=new Set;for(let s=0;se?this._bufferService.cols:s.link.range.end.x;for(let e=n;e<=o;e++){if(i.has(e)){r.splice(t--,1);break}i.add(e)}}}}_checkLinkProviderResult(e,t,i){var s;if(!this._activeProviderReplies)return i;const r=this._activeProviderReplies.get(e);let n=!1;for(let t=0;tthis._linkAtPosition(e.link,t)));e&&(i=!0,this._handleNewLink(e))}if(this._activeProviderReplies.size===this._linkProviders.length&&!i)for(let e=0;ethis._linkAtPosition(e.link,t)));if(r){i=!0,this._handleNewLink(r);break}}return i}_handleMouseDown(){this._mouseDownLink=this._currentLink}_handleMouseUp(e){if(!this._element||!this._mouseService||!this._currentLink)return;const t=this._positionFromMouseEvent(e,this._element,this._mouseService);t&&this._mouseDownLink===this._currentLink&&this._linkAtPosition(this._currentLink.link,t)&&this._currentLink.link.activate(e,this._currentLink.link.text)}_clearCurrentLink(e,t){this._element&&this._currentLink&&this._lastMouseEvent&&(!e||!t||this._currentLink.link.range.start.y>=e&&this._currentLink.link.range.end.y<=t)&&(this._linkLeave(this._element,this._currentLink.link,this._lastMouseEvent),this._currentLink=void 0,(0,a.disposeArray)(this._linkCacheDisposables))}_handleNewLink(e){if(!this._element||!this._lastMouseEvent||!this._mouseService)return;const t=this._positionFromMouseEvent(this._lastMouseEvent,this._element,this._mouseService);t&&this._linkAtPosition(e.link,t)&&(this._currentLink=e,this._currentLink.state={decorations:{underline:void 0===e.link.decorations||e.link.decorations.underline,pointerCursor:void 0===e.link.decorations||e.link.decorations.pointerCursor},isHovered:!0},this._linkHover(this._element,e.link,this._lastMouseEvent),e.link.decorations={},Object.defineProperties(e.link.decorations,{pointerCursor:{get:()=>{var e,t;return null===(t=null===(e=this._currentLink)||void 0===e?void 0:e.state)||void 0===t?void 0:t.decorations.pointerCursor},set:e=>{var t,i;(null===(t=this._currentLink)||void 0===t?void 0:t.state)&&this._currentLink.state.decorations.pointerCursor!==e&&(this._currentLink.state.decorations.pointerCursor=e,this._currentLink.state.isHovered&&(null===(i=this._element)||void 0===i||i.classList.toggle("xterm-cursor-pointer",e)))}},underline:{get:()=>{var e,t;return null===(t=null===(e=this._currentLink)||void 0===e?void 0:e.state)||void 0===t?void 0:t.decorations.underline},set:t=>{var i,s,r;(null===(i=this._currentLink)||void 0===i?void 0:i.state)&&(null===(r=null===(s=this._currentLink)||void 0===s?void 0:s.state)||void 0===r?void 0:r.decorations.underline)!==t&&(this._currentLink.state.decorations.underline=t,this._currentLink.state.isHovered&&this._fireUnderlineEvent(e.link,t))}}}),this._renderService&&this._linkCacheDisposables.push(this._renderService.onRenderedViewportChange((e=>{if(!this._currentLink)return;const t=0===e.start?0:e.start+1+this._bufferService.buffer.ydisp,i=this._bufferService.buffer.ydisp+1+e.end;if(this._currentLink.link.range.start.y>=t&&this._currentLink.link.range.end.y<=i&&(this._clearCurrentLink(t,i),this._lastMouseEvent&&this._element)){const e=this._positionFromMouseEvent(this._lastMouseEvent,this._element,this._mouseService);e&&this._askForLink(e,!1)}}))))}_linkHover(e,t,i){var s;(null===(s=this._currentLink)||void 0===s?void 0:s.state)&&(this._currentLink.state.isHovered=!0,this._currentLink.state.decorations.underline&&this._fireUnderlineEvent(t,!0),this._currentLink.state.decorations.pointerCursor&&e.classList.add("xterm-cursor-pointer")),t.hover&&t.hover(i,t.text)}_fireUnderlineEvent(e,t){const i=e.range,s=this._bufferService.buffer.ydisp,r=this._createLinkUnderlineEvent(i.start.x-1,i.start.y-s-1,i.end.x,i.end.y-s-1,void 0);(t?this._onShowLinkUnderline:this._onHideLinkUnderline).fire(r)}_linkLeave(e,t,i){var s;(null===(s=this._currentLink)||void 0===s?void 0:s.state)&&(this._currentLink.state.isHovered=!1,this._currentLink.state.decorations.underline&&this._fireUnderlineEvent(t,!1),this._currentLink.state.decorations.pointerCursor&&e.classList.remove("xterm-cursor-pointer")),t.leave&&t.leave(i,t.text)}_linkAtPosition(e,t){const i=e.range.start.y*this._bufferService.cols+e.range.start.x,s=e.range.end.y*this._bufferService.cols+e.range.end.x,r=t.y*this._bufferService.cols+t.x;return i<=r&&r<=s}_positionFromMouseEvent(e,t,i){const s=i.getCoords(e,t,this._bufferService.cols,this._bufferService.rows);if(s)return{x:s[0],y:s[1]+this._bufferService.buffer.ydisp}}_createLinkUnderlineEvent(e,t,i,s,r){return{x1:e,y1:t,x2:i,y2:s,cols:this._bufferService.cols,fg:r}}};t.Linkifier2=c=s([r(0,h.IBufferService)],c)},9042:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.tooMuchOutput=t.promptLabel=void 0,t.promptLabel="Terminal input",t.tooMuchOutput="Too much output to announce, navigate to rows manually to read"},3730:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.OscLinkProvider=void 0;const n=i(511),o=i(2585);let a=t.OscLinkProvider=class{constructor(e,t,i){this._bufferService=e,this._optionsService=t,this._oscLinkService=i}provideLinks(e,t){var i;const s=this._bufferService.buffer.lines.get(e-1);if(!s)return void t(void 0);const r=[],o=this._optionsService.rawOptions.linkHandler,a=new n.CellData,c=s.getTrimmedLength();let l=-1,d=-1,_=!1;for(let t=0;to?o.activate(e,t,i):h(0,t),hover:(e,t)=>{var s;return null===(s=null==o?void 0:o.hover)||void 0===s?void 0:s.call(o,e,t,i)},leave:(e,t)=>{var s;return null===(s=null==o?void 0:o.leave)||void 0===s?void 0:s.call(o,e,t,i)}})}_=!1,a.hasExtendedAttrs()&&a.extended.urlId?(d=t,l=a.extended.urlId):(d=-1,l=-1)}}t(r)}};function h(e,t){if(confirm(`Do you want to navigate to ${t}?\n\nWARNING: This link could potentially be dangerous`)){const e=window.open();if(e){try{e.opener=null}catch(e){}e.location.href=t}else console.warn("Opening link blocked as opener could not be cleared")}}t.OscLinkProvider=a=s([r(0,o.IBufferService),r(1,o.IOptionsService),r(2,o.IOscLinkService)],a)},6193:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.RenderDebouncer=void 0,t.RenderDebouncer=class{constructor(e,t){this._parentWindow=e,this._renderCallback=t,this._refreshCallbacks=[]}dispose(){this._animationFrame&&(this._parentWindow.cancelAnimationFrame(this._animationFrame),this._animationFrame=void 0)}addRefreshCallback(e){return this._refreshCallbacks.push(e),this._animationFrame||(this._animationFrame=this._parentWindow.requestAnimationFrame((()=>this._innerRefresh()))),this._animationFrame}refresh(e,t,i){this._rowCount=i,e=void 0!==e?e:0,t=void 0!==t?t:this._rowCount-1,this._rowStart=void 0!==this._rowStart?Math.min(this._rowStart,e):e,this._rowEnd=void 0!==this._rowEnd?Math.max(this._rowEnd,t):t,this._animationFrame||(this._animationFrame=this._parentWindow.requestAnimationFrame((()=>this._innerRefresh())))}_innerRefresh(){if(this._animationFrame=void 0,void 0===this._rowStart||void 0===this._rowEnd||void 0===this._rowCount)return void this._runRefreshCallbacks();const e=Math.max(this._rowStart,0),t=Math.min(this._rowEnd,this._rowCount-1);this._rowStart=void 0,this._rowEnd=void 0,this._renderCallback(e,t),this._runRefreshCallbacks()}_runRefreshCallbacks(){for(const e of this._refreshCallbacks)e(0);this._refreshCallbacks=[]}}},5596:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.ScreenDprMonitor=void 0;const s=i(844);class r extends s.Disposable{constructor(e){super(),this._parentWindow=e,this._currentDevicePixelRatio=this._parentWindow.devicePixelRatio,this.register((0,s.toDisposable)((()=>{this.clearListener()})))}setListener(e){this._listener&&this.clearListener(),this._listener=e,this._outerListener=()=>{this._listener&&(this._listener(this._parentWindow.devicePixelRatio,this._currentDevicePixelRatio),this._updateDpr())},this._updateDpr()}_updateDpr(){var e;this._outerListener&&(null===(e=this._resolutionMediaMatchList)||void 0===e||e.removeListener(this._outerListener),this._currentDevicePixelRatio=this._parentWindow.devicePixelRatio,this._resolutionMediaMatchList=this._parentWindow.matchMedia(`screen and (resolution: ${this._parentWindow.devicePixelRatio}dppx)`),this._resolutionMediaMatchList.addListener(this._outerListener))}clearListener(){this._resolutionMediaMatchList&&this._listener&&this._outerListener&&(this._resolutionMediaMatchList.removeListener(this._outerListener),this._resolutionMediaMatchList=void 0,this._listener=void 0,this._outerListener=void 0)}}t.ScreenDprMonitor=r},3236:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.Terminal=void 0;const s=i(3614),r=i(3656),n=i(6465),o=i(9042),a=i(3730),h=i(1680),c=i(3107),l=i(5744),d=i(2950),_=i(1296),u=i(428),f=i(4269),v=i(5114),p=i(8934),g=i(3230),m=i(9312),S=i(4725),C=i(6731),b=i(8055),y=i(8969),w=i(8460),E=i(844),k=i(6114),L=i(8437),D=i(2584),R=i(7399),x=i(5941),A=i(9074),B=i(2585),T=i(5435),M=i(4567),O="undefined"!=typeof window?window.document:null;class P extends y.CoreTerminal{get onFocus(){return this._onFocus.event}get onBlur(){return this._onBlur.event}get onA11yChar(){return this._onA11yCharEmitter.event}get onA11yTab(){return this._onA11yTabEmitter.event}get onWillOpen(){return this._onWillOpen.event}constructor(e={}){super(e),this.browser=k,this._keyDownHandled=!1,this._keyDownSeen=!1,this._keyPressHandled=!1,this._unprocessedDeadKey=!1,this._accessibilityManager=this.register(new E.MutableDisposable),this._onCursorMove=this.register(new w.EventEmitter),this.onCursorMove=this._onCursorMove.event,this._onKey=this.register(new w.EventEmitter),this.onKey=this._onKey.event,this._onRender=this.register(new w.EventEmitter),this.onRender=this._onRender.event,this._onSelectionChange=this.register(new w.EventEmitter),this.onSelectionChange=this._onSelectionChange.event,this._onTitleChange=this.register(new w.EventEmitter),this.onTitleChange=this._onTitleChange.event,this._onBell=this.register(new w.EventEmitter),this.onBell=this._onBell.event,this._onFocus=this.register(new w.EventEmitter),this._onBlur=this.register(new w.EventEmitter),this._onA11yCharEmitter=this.register(new w.EventEmitter),this._onA11yTabEmitter=this.register(new w.EventEmitter),this._onWillOpen=this.register(new w.EventEmitter),this._setup(),this.linkifier2=this.register(this._instantiationService.createInstance(n.Linkifier2)),this.linkifier2.registerLinkProvider(this._instantiationService.createInstance(a.OscLinkProvider)),this._decorationService=this._instantiationService.createInstance(A.DecorationService),this._instantiationService.setService(B.IDecorationService,this._decorationService),this.register(this._inputHandler.onRequestBell((()=>this._onBell.fire()))),this.register(this._inputHandler.onRequestRefreshRows(((e,t)=>this.refresh(e,t)))),this.register(this._inputHandler.onRequestSendFocus((()=>this._reportFocus()))),this.register(this._inputHandler.onRequestReset((()=>this.reset()))),this.register(this._inputHandler.onRequestWindowsOptionsReport((e=>this._reportWindowsOptions(e)))),this.register(this._inputHandler.onColor((e=>this._handleColorEvent(e)))),this.register((0,w.forwardEvent)(this._inputHandler.onCursorMove,this._onCursorMove)),this.register((0,w.forwardEvent)(this._inputHandler.onTitleChange,this._onTitleChange)),this.register((0,w.forwardEvent)(this._inputHandler.onA11yChar,this._onA11yCharEmitter)),this.register((0,w.forwardEvent)(this._inputHandler.onA11yTab,this._onA11yTabEmitter)),this.register(this._bufferService.onResize((e=>this._afterResize(e.cols,e.rows)))),this.register((0,E.toDisposable)((()=>{var e,t;this._customKeyEventHandler=void 0,null===(t=null===(e=this.element)||void 0===e?void 0:e.parentNode)||void 0===t||t.removeChild(this.element)})))}_handleColorEvent(e){if(this._themeService)for(const t of e){let e,i="";switch(t.index){case 256:e="foreground",i="10";break;case 257:e="background",i="11";break;case 258:e="cursor",i="12";break;default:e="ansi",i="4;"+t.index}switch(t.type){case 0:const s=b.color.toColorRGB("ansi"===e?this._themeService.colors.ansi[t.index]:this._themeService.colors[e]);this.coreService.triggerDataEvent(`${D.C0.ESC}]${i};${(0,x.toRgbString)(s)}${D.C1_ESCAPED.ST}`);break;case 1:if("ansi"===e)this._themeService.modifyColors((e=>e.ansi[t.index]=b.rgba.toColor(...t.color)));else{const i=e;this._themeService.modifyColors((e=>e[i]=b.rgba.toColor(...t.color)))}break;case 2:this._themeService.restoreColor(t.index)}}}_setup(){super._setup(),this._customKeyEventHandler=void 0}get buffer(){return this.buffers.active}focus(){this.textarea&&this.textarea.focus({preventScroll:!0})}_handleScreenReaderModeOptionChange(e){e?!this._accessibilityManager.value&&this._renderService&&(this._accessibilityManager.value=this._instantiationService.createInstance(M.AccessibilityManager,this)):this._accessibilityManager.clear()}_handleTextAreaFocus(e){this.coreService.decPrivateModes.sendFocus&&this.coreService.triggerDataEvent(D.C0.ESC+"[I"),this.updateCursorStyle(e),this.element.classList.add("focus"),this._showCursor(),this._onFocus.fire()}blur(){var e;return null===(e=this.textarea)||void 0===e?void 0:e.blur()}_handleTextAreaBlur(){this.textarea.value="",this.refresh(this.buffer.y,this.buffer.y),this.coreService.decPrivateModes.sendFocus&&this.coreService.triggerDataEvent(D.C0.ESC+"[O"),this.element.classList.remove("focus"),this._onBlur.fire()}_syncTextArea(){if(!this.textarea||!this.buffer.isCursorInViewport||this._compositionHelper.isComposing||!this._renderService)return;const e=this.buffer.ybase+this.buffer.y,t=this.buffer.lines.get(e);if(!t)return;const i=Math.min(this.buffer.x,this.cols-1),s=this._renderService.dimensions.css.cell.height,r=t.getWidth(i),n=this._renderService.dimensions.css.cell.width*r,o=this.buffer.y*this._renderService.dimensions.css.cell.height,a=i*this._renderService.dimensions.css.cell.width;this.textarea.style.left=a+"px",this.textarea.style.top=o+"px",this.textarea.style.width=n+"px",this.textarea.style.height=s+"px",this.textarea.style.lineHeight=s+"px",this.textarea.style.zIndex="-5"}_initGlobal(){this._bindKeys(),this.register((0,r.addDisposableDomListener)(this.element,"copy",(e=>{this.hasSelection()&&(0,s.copyHandler)(e,this._selectionService)})));const e=e=>(0,s.handlePasteEvent)(e,this.textarea,this.coreService,this.optionsService);this.register((0,r.addDisposableDomListener)(this.textarea,"paste",e)),this.register((0,r.addDisposableDomListener)(this.element,"paste",e)),k.isFirefox?this.register((0,r.addDisposableDomListener)(this.element,"mousedown",(e=>{2===e.button&&(0,s.rightClickHandler)(e,this.textarea,this.screenElement,this._selectionService,this.options.rightClickSelectsWord)}))):this.register((0,r.addDisposableDomListener)(this.element,"contextmenu",(e=>{(0,s.rightClickHandler)(e,this.textarea,this.screenElement,this._selectionService,this.options.rightClickSelectsWord)}))),k.isLinux&&this.register((0,r.addDisposableDomListener)(this.element,"auxclick",(e=>{1===e.button&&(0,s.moveTextAreaUnderMouseCursor)(e,this.textarea,this.screenElement)})))}_bindKeys(){this.register((0,r.addDisposableDomListener)(this.textarea,"keyup",(e=>this._keyUp(e)),!0)),this.register((0,r.addDisposableDomListener)(this.textarea,"keydown",(e=>this._keyDown(e)),!0)),this.register((0,r.addDisposableDomListener)(this.textarea,"keypress",(e=>this._keyPress(e)),!0)),this.register((0,r.addDisposableDomListener)(this.textarea,"compositionstart",(()=>this._compositionHelper.compositionstart()))),this.register((0,r.addDisposableDomListener)(this.textarea,"compositionupdate",(e=>this._compositionHelper.compositionupdate(e)))),this.register((0,r.addDisposableDomListener)(this.textarea,"compositionend",(()=>this._compositionHelper.compositionend()))),this.register((0,r.addDisposableDomListener)(this.textarea,"input",(e=>this._inputEvent(e)),!0)),this.register(this.onRender((()=>this._compositionHelper.updateCompositionElements())))}open(e){var t;if(!e)throw new Error("Terminal requires a parent element.");e.isConnected||this._logService.debug("Terminal.open was called on an element that was not attached to the DOM"),this._document=e.ownerDocument,this.element=this._document.createElement("div"),this.element.dir="ltr",this.element.classList.add("terminal"),this.element.classList.add("xterm"),e.appendChild(this.element);const i=O.createDocumentFragment();this._viewportElement=O.createElement("div"),this._viewportElement.classList.add("xterm-viewport"),i.appendChild(this._viewportElement),this._viewportScrollArea=O.createElement("div"),this._viewportScrollArea.classList.add("xterm-scroll-area"),this._viewportElement.appendChild(this._viewportScrollArea),this.screenElement=O.createElement("div"),this.screenElement.classList.add("xterm-screen"),this._helperContainer=O.createElement("div"),this._helperContainer.classList.add("xterm-helpers"),this.screenElement.appendChild(this._helperContainer),i.appendChild(this.screenElement),this.textarea=O.createElement("textarea"),this.textarea.classList.add("xterm-helper-textarea"),this.textarea.setAttribute("aria-label",o.promptLabel),k.isChromeOS||this.textarea.setAttribute("aria-multiline","false"),this.textarea.setAttribute("autocorrect","off"),this.textarea.setAttribute("autocapitalize","off"),this.textarea.setAttribute("spellcheck","false"),this.textarea.tabIndex=0,this._coreBrowserService=this._instantiationService.createInstance(v.CoreBrowserService,this.textarea,null!==(t=this._document.defaultView)&&void 0!==t?t:window),this._instantiationService.setService(S.ICoreBrowserService,this._coreBrowserService),this.register((0,r.addDisposableDomListener)(this.textarea,"focus",(e=>this._handleTextAreaFocus(e)))),this.register((0,r.addDisposableDomListener)(this.textarea,"blur",(()=>this._handleTextAreaBlur()))),this._helperContainer.appendChild(this.textarea),this._charSizeService=this._instantiationService.createInstance(u.CharSizeService,this._document,this._helperContainer),this._instantiationService.setService(S.ICharSizeService,this._charSizeService),this._themeService=this._instantiationService.createInstance(C.ThemeService),this._instantiationService.setService(S.IThemeService,this._themeService),this._characterJoinerService=this._instantiationService.createInstance(f.CharacterJoinerService),this._instantiationService.setService(S.ICharacterJoinerService,this._characterJoinerService),this._renderService=this.register(this._instantiationService.createInstance(g.RenderService,this.rows,this.screenElement)),this._instantiationService.setService(S.IRenderService,this._renderService),this.register(this._renderService.onRenderedViewportChange((e=>this._onRender.fire(e)))),this.onResize((e=>this._renderService.resize(e.cols,e.rows))),this._compositionView=O.createElement("div"),this._compositionView.classList.add("composition-view"),this._compositionHelper=this._instantiationService.createInstance(d.CompositionHelper,this.textarea,this._compositionView),this._helperContainer.appendChild(this._compositionView),this.element.appendChild(i);try{this._onWillOpen.fire(this.element)}catch(e){}this._renderService.hasRenderer()||this._renderService.setRenderer(this._createRenderer()),this._mouseService=this._instantiationService.createInstance(p.MouseService),this._instantiationService.setService(S.IMouseService,this._mouseService),this.viewport=this._instantiationService.createInstance(h.Viewport,this._viewportElement,this._viewportScrollArea),this.viewport.onRequestScrollLines((e=>this.scrollLines(e.amount,e.suppressScrollEvent,1))),this.register(this._inputHandler.onRequestSyncScrollBar((()=>this.viewport.syncScrollArea()))),this.register(this.viewport),this.register(this.onCursorMove((()=>{this._renderService.handleCursorMove(),this._syncTextArea()}))),this.register(this.onResize((()=>this._renderService.handleResize(this.cols,this.rows)))),this.register(this.onBlur((()=>this._renderService.handleBlur()))),this.register(this.onFocus((()=>this._renderService.handleFocus()))),this.register(this._renderService.onDimensionsChange((()=>this.viewport.syncScrollArea()))),this._selectionService=this.register(this._instantiationService.createInstance(m.SelectionService,this.element,this.screenElement,this.linkifier2)),this._instantiationService.setService(S.ISelectionService,this._selectionService),this.register(this._selectionService.onRequestScrollLines((e=>this.scrollLines(e.amount,e.suppressScrollEvent)))),this.register(this._selectionService.onSelectionChange((()=>this._onSelectionChange.fire()))),this.register(this._selectionService.onRequestRedraw((e=>this._renderService.handleSelectionChanged(e.start,e.end,e.columnSelectMode)))),this.register(this._selectionService.onLinuxMouseSelection((e=>{this.textarea.value=e,this.textarea.focus(),this.textarea.select()}))),this.register(this._onScroll.event((e=>{this.viewport.syncScrollArea(),this._selectionService.refresh()}))),this.register((0,r.addDisposableDomListener)(this._viewportElement,"scroll",(()=>this._selectionService.refresh()))),this.linkifier2.attachToDom(this.screenElement,this._mouseService,this._renderService),this.register(this._instantiationService.createInstance(c.BufferDecorationRenderer,this.screenElement)),this.register((0,r.addDisposableDomListener)(this.element,"mousedown",(e=>this._selectionService.handleMouseDown(e)))),this.coreMouseService.areMouseEventsActive?(this._selectionService.disable(),this.element.classList.add("enable-mouse-events")):this._selectionService.enable(),this.options.screenReaderMode&&(this._accessibilityManager.value=this._instantiationService.createInstance(M.AccessibilityManager,this)),this.register(this.optionsService.onSpecificOptionChange("screenReaderMode",(e=>this._handleScreenReaderModeOptionChange(e)))),this.options.overviewRulerWidth&&(this._overviewRulerRenderer=this.register(this._instantiationService.createInstance(l.OverviewRulerRenderer,this._viewportElement,this.screenElement))),this.optionsService.onSpecificOptionChange("overviewRulerWidth",(e=>{!this._overviewRulerRenderer&&e&&this._viewportElement&&this.screenElement&&(this._overviewRulerRenderer=this.register(this._instantiationService.createInstance(l.OverviewRulerRenderer,this._viewportElement,this.screenElement)))})),this._charSizeService.measure(),this.refresh(0,this.rows-1),this._initGlobal(),this.bindMouse()}_createRenderer(){return this._instantiationService.createInstance(_.DomRenderer,this.element,this.screenElement,this._viewportElement,this.linkifier2)}bindMouse(){const e=this,t=this.element;function i(t){const i=e._mouseService.getMouseReportCoords(t,e.screenElement);if(!i)return!1;let s,r;switch(t.overrideType||t.type){case"mousemove":r=32,void 0===t.buttons?(s=3,void 0!==t.button&&(s=t.button<3?t.button:3)):s=1&t.buttons?0:4&t.buttons?1:2&t.buttons?2:3;break;case"mouseup":r=0,s=t.button<3?t.button:3;break;case"mousedown":r=1,s=t.button<3?t.button:3;break;case"wheel":if(0===e.viewport.getLinesScrolled(t))return!1;r=t.deltaY<0?0:1,s=4;break;default:return!1}return!(void 0===r||void 0===s||s>4)&&e.coreMouseService.triggerMouseEvent({col:i.col,row:i.row,x:i.x,y:i.y,button:s,action:r,ctrl:t.ctrlKey,alt:t.altKey,shift:t.shiftKey})}const s={mouseup:null,wheel:null,mousedrag:null,mousemove:null},n={mouseup:e=>(i(e),e.buttons||(this._document.removeEventListener("mouseup",s.mouseup),s.mousedrag&&this._document.removeEventListener("mousemove",s.mousedrag)),this.cancel(e)),wheel:e=>(i(e),this.cancel(e,!0)),mousedrag:e=>{e.buttons&&i(e)},mousemove:e=>{e.buttons||i(e)}};this.register(this.coreMouseService.onProtocolChange((e=>{e?("debug"===this.optionsService.rawOptions.logLevel&&this._logService.debug("Binding to mouse events:",this.coreMouseService.explainEvents(e)),this.element.classList.add("enable-mouse-events"),this._selectionService.disable()):(this._logService.debug("Unbinding from mouse events."),this.element.classList.remove("enable-mouse-events"),this._selectionService.enable()),8&e?s.mousemove||(t.addEventListener("mousemove",n.mousemove),s.mousemove=n.mousemove):(t.removeEventListener("mousemove",s.mousemove),s.mousemove=null),16&e?s.wheel||(t.addEventListener("wheel",n.wheel,{passive:!1}),s.wheel=n.wheel):(t.removeEventListener("wheel",s.wheel),s.wheel=null),2&e?s.mouseup||(t.addEventListener("mouseup",n.mouseup),s.mouseup=n.mouseup):(this._document.removeEventListener("mouseup",s.mouseup),t.removeEventListener("mouseup",s.mouseup),s.mouseup=null),4&e?s.mousedrag||(s.mousedrag=n.mousedrag):(this._document.removeEventListener("mousemove",s.mousedrag),s.mousedrag=null)}))),this.coreMouseService.activeProtocol=this.coreMouseService.activeProtocol,this.register((0,r.addDisposableDomListener)(t,"mousedown",(e=>{if(e.preventDefault(),this.focus(),this.coreMouseService.areMouseEventsActive&&!this._selectionService.shouldForceSelection(e))return i(e),s.mouseup&&this._document.addEventListener("mouseup",s.mouseup),s.mousedrag&&this._document.addEventListener("mousemove",s.mousedrag),this.cancel(e)}))),this.register((0,r.addDisposableDomListener)(t,"wheel",(e=>{if(!s.wheel){if(!this.buffer.hasScrollback){const t=this.viewport.getLinesScrolled(e);if(0===t)return;const i=D.C0.ESC+(this.coreService.decPrivateModes.applicationCursorKeys?"O":"[")+(e.deltaY<0?"A":"B");let s="";for(let e=0;e{if(!this.coreMouseService.areMouseEventsActive)return this.viewport.handleTouchStart(e),this.cancel(e)}),{passive:!0})),this.register((0,r.addDisposableDomListener)(t,"touchmove",(e=>{if(!this.coreMouseService.areMouseEventsActive)return this.viewport.handleTouchMove(e)?void 0:this.cancel(e)}),{passive:!1}))}refresh(e,t){var i;null===(i=this._renderService)||void 0===i||i.refreshRows(e,t)}updateCursorStyle(e){var t;(null===(t=this._selectionService)||void 0===t?void 0:t.shouldColumnSelect(e))?this.element.classList.add("column-select"):this.element.classList.remove("column-select")}_showCursor(){this.coreService.isCursorInitialized||(this.coreService.isCursorInitialized=!0,this.refresh(this.buffer.y,this.buffer.y))}scrollLines(e,t,i=0){var s;1===i?(super.scrollLines(e,t,i),this.refresh(0,this.rows-1)):null===(s=this.viewport)||void 0===s||s.scrollLines(e)}paste(e){(0,s.paste)(e,this.textarea,this.coreService,this.optionsService)}attachCustomKeyEventHandler(e){this._customKeyEventHandler=e}registerLinkProvider(e){return this.linkifier2.registerLinkProvider(e)}registerCharacterJoiner(e){if(!this._characterJoinerService)throw new Error("Terminal must be opened first");const t=this._characterJoinerService.register(e);return this.refresh(0,this.rows-1),t}deregisterCharacterJoiner(e){if(!this._characterJoinerService)throw new Error("Terminal must be opened first");this._characterJoinerService.deregister(e)&&this.refresh(0,this.rows-1)}get markers(){return this.buffer.markers}registerMarker(e){return this.buffer.addMarker(this.buffer.ybase+this.buffer.y+e)}registerDecoration(e){return this._decorationService.registerDecoration(e)}hasSelection(){return!!this._selectionService&&this._selectionService.hasSelection}select(e,t,i){this._selectionService.setSelection(e,t,i)}getSelection(){return this._selectionService?this._selectionService.selectionText:""}getSelectionPosition(){if(this._selectionService&&this._selectionService.hasSelection)return{start:{x:this._selectionService.selectionStart[0],y:this._selectionService.selectionStart[1]},end:{x:this._selectionService.selectionEnd[0],y:this._selectionService.selectionEnd[1]}}}clearSelection(){var e;null===(e=this._selectionService)||void 0===e||e.clearSelection()}selectAll(){var e;null===(e=this._selectionService)||void 0===e||e.selectAll()}selectLines(e,t){var i;null===(i=this._selectionService)||void 0===i||i.selectLines(e,t)}_keyDown(e){if(this._keyDownHandled=!1,this._keyDownSeen=!0,this._customKeyEventHandler&&!1===this._customKeyEventHandler(e))return!1;const t=this.browser.isMac&&this.options.macOptionIsMeta&&e.altKey;if(!t&&!this._compositionHelper.keydown(e))return this.options.scrollOnUserInput&&this.buffer.ybase!==this.buffer.ydisp&&this.scrollToBottom(),!1;t||"Dead"!==e.key&&"AltGraph"!==e.key||(this._unprocessedDeadKey=!0);const i=(0,R.evaluateKeyboardEvent)(e,this.coreService.decPrivateModes.applicationCursorKeys,this.browser.isMac,this.options.macOptionIsMeta);if(this.updateCursorStyle(e),3===i.type||2===i.type){const t=this.rows-1;return this.scrollLines(2===i.type?-t:t),this.cancel(e,!0)}return 1===i.type&&this.selectAll(),!!this._isThirdLevelShift(this.browser,e)||(i.cancel&&this.cancel(e,!0),!i.key||!!(e.key&&!e.ctrlKey&&!e.altKey&&!e.metaKey&&1===e.key.length&&e.key.charCodeAt(0)>=65&&e.key.charCodeAt(0)<=90)||(this._unprocessedDeadKey?(this._unprocessedDeadKey=!1,!0):(i.key!==D.C0.ETX&&i.key!==D.C0.CR||(this.textarea.value=""),this._onKey.fire({key:i.key,domEvent:e}),this._showCursor(),this.coreService.triggerDataEvent(i.key,!0),!this.optionsService.rawOptions.screenReaderMode||e.altKey||e.ctrlKey?this.cancel(e,!0):void(this._keyDownHandled=!0))))}_isThirdLevelShift(e,t){const i=e.isMac&&!this.options.macOptionIsMeta&&t.altKey&&!t.ctrlKey&&!t.metaKey||e.isWindows&&t.altKey&&t.ctrlKey&&!t.metaKey||e.isWindows&&t.getModifierState("AltGraph");return"keypress"===t.type?i:i&&(!t.keyCode||t.keyCode>47)}_keyUp(e){this._keyDownSeen=!1,this._customKeyEventHandler&&!1===this._customKeyEventHandler(e)||(function(e){return 16===e.keyCode||17===e.keyCode||18===e.keyCode}(e)||this.focus(),this.updateCursorStyle(e),this._keyPressHandled=!1)}_keyPress(e){let t;if(this._keyPressHandled=!1,this._keyDownHandled)return!1;if(this._customKeyEventHandler&&!1===this._customKeyEventHandler(e))return!1;if(this.cancel(e),e.charCode)t=e.charCode;else if(null===e.which||void 0===e.which)t=e.keyCode;else{if(0===e.which||0===e.charCode)return!1;t=e.which}return!(!t||(e.altKey||e.ctrlKey||e.metaKey)&&!this._isThirdLevelShift(this.browser,e)||(t=String.fromCharCode(t),this._onKey.fire({key:t,domEvent:e}),this._showCursor(),this.coreService.triggerDataEvent(t,!0),this._keyPressHandled=!0,this._unprocessedDeadKey=!1,0))}_inputEvent(e){if(e.data&&"insertText"===e.inputType&&(!e.composed||!this._keyDownSeen)&&!this.optionsService.rawOptions.screenReaderMode){if(this._keyPressHandled)return!1;this._unprocessedDeadKey=!1;const t=e.data;return this.coreService.triggerDataEvent(t,!0),this.cancel(e),!0}return!1}resize(e,t){e!==this.cols||t!==this.rows?super.resize(e,t):this._charSizeService&&!this._charSizeService.hasValidSize&&this._charSizeService.measure()}_afterResize(e,t){var i,s;null===(i=this._charSizeService)||void 0===i||i.measure(),null===(s=this.viewport)||void 0===s||s.syncScrollArea(!0)}clear(){var e;if(0!==this.buffer.ybase||0!==this.buffer.y){this.buffer.clearAllMarkers(),this.buffer.lines.set(0,this.buffer.lines.get(this.buffer.ybase+this.buffer.y)),this.buffer.lines.length=1,this.buffer.ydisp=0,this.buffer.ybase=0,this.buffer.y=0;for(let e=1;e{Object.defineProperty(t,"__esModule",{value:!0}),t.TimeBasedDebouncer=void 0,t.TimeBasedDebouncer=class{constructor(e,t=1e3){this._renderCallback=e,this._debounceThresholdMS=t,this._lastRefreshMs=0,this._additionalRefreshRequested=!1}dispose(){this._refreshTimeoutID&&clearTimeout(this._refreshTimeoutID)}refresh(e,t,i){this._rowCount=i,e=void 0!==e?e:0,t=void 0!==t?t:this._rowCount-1,this._rowStart=void 0!==this._rowStart?Math.min(this._rowStart,e):e,this._rowEnd=void 0!==this._rowEnd?Math.max(this._rowEnd,t):t;const s=Date.now();if(s-this._lastRefreshMs>=this._debounceThresholdMS)this._lastRefreshMs=s,this._innerRefresh();else if(!this._additionalRefreshRequested){const e=s-this._lastRefreshMs,t=this._debounceThresholdMS-e;this._additionalRefreshRequested=!0,this._refreshTimeoutID=window.setTimeout((()=>{this._lastRefreshMs=Date.now(),this._innerRefresh(),this._additionalRefreshRequested=!1,this._refreshTimeoutID=void 0}),t)}}_innerRefresh(){if(void 0===this._rowStart||void 0===this._rowEnd||void 0===this._rowCount)return;const e=Math.max(this._rowStart,0),t=Math.min(this._rowEnd,this._rowCount-1);this._rowStart=void 0,this._rowEnd=void 0,this._renderCallback(e,t)}}},1680:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.Viewport=void 0;const n=i(3656),o=i(4725),a=i(8460),h=i(844),c=i(2585);let l=t.Viewport=class extends h.Disposable{constructor(e,t,i,s,r,o,h,c){super(),this._viewportElement=e,this._scrollArea=t,this._bufferService=i,this._optionsService=s,this._charSizeService=r,this._renderService=o,this._coreBrowserService=h,this.scrollBarWidth=0,this._currentRowHeight=0,this._currentDeviceCellHeight=0,this._lastRecordedBufferLength=0,this._lastRecordedViewportHeight=0,this._lastRecordedBufferHeight=0,this._lastTouchY=0,this._lastScrollTop=0,this._wheelPartialScroll=0,this._refreshAnimationFrame=null,this._ignoreNextScrollEvent=!1,this._smoothScrollState={startTime:0,origin:-1,target:-1},this._onRequestScrollLines=this.register(new a.EventEmitter),this.onRequestScrollLines=this._onRequestScrollLines.event,this.scrollBarWidth=this._viewportElement.offsetWidth-this._scrollArea.offsetWidth||15,this.register((0,n.addDisposableDomListener)(this._viewportElement,"scroll",this._handleScroll.bind(this))),this._activeBuffer=this._bufferService.buffer,this.register(this._bufferService.buffers.onBufferActivate((e=>this._activeBuffer=e.activeBuffer))),this._renderDimensions=this._renderService.dimensions,this.register(this._renderService.onDimensionsChange((e=>this._renderDimensions=e))),this._handleThemeChange(c.colors),this.register(c.onChangeColors((e=>this._handleThemeChange(e)))),this.register(this._optionsService.onSpecificOptionChange("scrollback",(()=>this.syncScrollArea()))),setTimeout((()=>this.syncScrollArea()))}_handleThemeChange(e){this._viewportElement.style.backgroundColor=e.background.css}reset(){this._currentRowHeight=0,this._currentDeviceCellHeight=0,this._lastRecordedBufferLength=0,this._lastRecordedViewportHeight=0,this._lastRecordedBufferHeight=0,this._lastTouchY=0,this._lastScrollTop=0,this._coreBrowserService.window.requestAnimationFrame((()=>this.syncScrollArea()))}_refresh(e){if(e)return this._innerRefresh(),void(null!==this._refreshAnimationFrame&&this._coreBrowserService.window.cancelAnimationFrame(this._refreshAnimationFrame));null===this._refreshAnimationFrame&&(this._refreshAnimationFrame=this._coreBrowserService.window.requestAnimationFrame((()=>this._innerRefresh())))}_innerRefresh(){if(this._charSizeService.height>0){this._currentRowHeight=this._renderService.dimensions.device.cell.height/this._coreBrowserService.dpr,this._currentDeviceCellHeight=this._renderService.dimensions.device.cell.height,this._lastRecordedViewportHeight=this._viewportElement.offsetHeight;const e=Math.round(this._currentRowHeight*this._lastRecordedBufferLength)+(this._lastRecordedViewportHeight-this._renderService.dimensions.css.canvas.height);this._lastRecordedBufferHeight!==e&&(this._lastRecordedBufferHeight=e,this._scrollArea.style.height=this._lastRecordedBufferHeight+"px")}const e=this._bufferService.buffer.ydisp*this._currentRowHeight;this._viewportElement.scrollTop!==e&&(this._ignoreNextScrollEvent=!0,this._viewportElement.scrollTop=e),this._refreshAnimationFrame=null}syncScrollArea(e=!1){if(this._lastRecordedBufferLength!==this._bufferService.buffer.lines.length)return this._lastRecordedBufferLength=this._bufferService.buffer.lines.length,void this._refresh(e);this._lastRecordedViewportHeight===this._renderService.dimensions.css.canvas.height&&this._lastScrollTop===this._activeBuffer.ydisp*this._currentRowHeight&&this._renderDimensions.device.cell.height===this._currentDeviceCellHeight||this._refresh(e)}_handleScroll(e){if(this._lastScrollTop=this._viewportElement.scrollTop,!this._viewportElement.offsetParent)return;if(this._ignoreNextScrollEvent)return this._ignoreNextScrollEvent=!1,void this._onRequestScrollLines.fire({amount:0,suppressScrollEvent:!0});const t=Math.round(this._lastScrollTop/this._currentRowHeight)-this._bufferService.buffer.ydisp;this._onRequestScrollLines.fire({amount:t,suppressScrollEvent:!0})}_smoothScroll(){if(this._isDisposed||-1===this._smoothScrollState.origin||-1===this._smoothScrollState.target)return;const e=this._smoothScrollPercent();this._viewportElement.scrollTop=this._smoothScrollState.origin+Math.round(e*(this._smoothScrollState.target-this._smoothScrollState.origin)),e<1?this._coreBrowserService.window.requestAnimationFrame((()=>this._smoothScroll())):this._clearSmoothScrollState()}_smoothScrollPercent(){return this._optionsService.rawOptions.smoothScrollDuration&&this._smoothScrollState.startTime?Math.max(Math.min((Date.now()-this._smoothScrollState.startTime)/this._optionsService.rawOptions.smoothScrollDuration,1),0):1}_clearSmoothScrollState(){this._smoothScrollState.startTime=0,this._smoothScrollState.origin=-1,this._smoothScrollState.target=-1}_bubbleScroll(e,t){const i=this._viewportElement.scrollTop+this._lastRecordedViewportHeight;return!(t<0&&0!==this._viewportElement.scrollTop||t>0&&i0&&(s=e),r=""}}return{bufferElements:n,cursorElement:s}}getLinesScrolled(e){if(0===e.deltaY||e.shiftKey)return 0;let t=this._applyScrollModifier(e.deltaY,e);return e.deltaMode===WheelEvent.DOM_DELTA_PIXEL?(t/=this._currentRowHeight+0,this._wheelPartialScroll+=t,t=Math.floor(Math.abs(this._wheelPartialScroll))*(this._wheelPartialScroll>0?1:-1),this._wheelPartialScroll%=1):e.deltaMode===WheelEvent.DOM_DELTA_PAGE&&(t*=this._bufferService.rows),t}_applyScrollModifier(e,t){const i=this._optionsService.rawOptions.fastScrollModifier;return"alt"===i&&t.altKey||"ctrl"===i&&t.ctrlKey||"shift"===i&&t.shiftKey?e*this._optionsService.rawOptions.fastScrollSensitivity*this._optionsService.rawOptions.scrollSensitivity:e*this._optionsService.rawOptions.scrollSensitivity}handleTouchStart(e){this._lastTouchY=e.touches[0].pageY}handleTouchMove(e){const t=this._lastTouchY-e.touches[0].pageY;return this._lastTouchY=e.touches[0].pageY,0!==t&&(this._viewportElement.scrollTop+=t,this._bubbleScroll(e,t))}};t.Viewport=l=s([r(2,c.IBufferService),r(3,c.IOptionsService),r(4,o.ICharSizeService),r(5,o.IRenderService),r(6,o.ICoreBrowserService),r(7,o.IThemeService)],l)},3107:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.BufferDecorationRenderer=void 0;const n=i(3656),o=i(4725),a=i(844),h=i(2585);let c=t.BufferDecorationRenderer=class extends a.Disposable{constructor(e,t,i,s){super(),this._screenElement=e,this._bufferService=t,this._decorationService=i,this._renderService=s,this._decorationElements=new Map,this._altBufferIsActive=!1,this._dimensionsChanged=!1,this._container=document.createElement("div"),this._container.classList.add("xterm-decoration-container"),this._screenElement.appendChild(this._container),this.register(this._renderService.onRenderedViewportChange((()=>this._doRefreshDecorations()))),this.register(this._renderService.onDimensionsChange((()=>{this._dimensionsChanged=!0,this._queueRefresh()}))),this.register((0,n.addDisposableDomListener)(window,"resize",(()=>this._queueRefresh()))),this.register(this._bufferService.buffers.onBufferActivate((()=>{this._altBufferIsActive=this._bufferService.buffer===this._bufferService.buffers.alt}))),this.register(this._decorationService.onDecorationRegistered((()=>this._queueRefresh()))),this.register(this._decorationService.onDecorationRemoved((e=>this._removeDecoration(e)))),this.register((0,a.toDisposable)((()=>{this._container.remove(),this._decorationElements.clear()})))}_queueRefresh(){void 0===this._animationFrame&&(this._animationFrame=this._renderService.addRefreshCallback((()=>{this._doRefreshDecorations(),this._animationFrame=void 0})))}_doRefreshDecorations(){for(const e of this._decorationService.decorations)this._renderDecoration(e);this._dimensionsChanged=!1}_renderDecoration(e){this._refreshStyle(e),this._dimensionsChanged&&this._refreshXPosition(e)}_createElement(e){var t,i;const s=document.createElement("div");s.classList.add("xterm-decoration"),s.classList.toggle("xterm-decoration-top-layer","top"===(null===(t=null==e?void 0:e.options)||void 0===t?void 0:t.layer)),s.style.width=`${Math.round((e.options.width||1)*this._renderService.dimensions.css.cell.width)}px`,s.style.height=(e.options.height||1)*this._renderService.dimensions.css.cell.height+"px",s.style.top=(e.marker.line-this._bufferService.buffers.active.ydisp)*this._renderService.dimensions.css.cell.height+"px",s.style.lineHeight=`${this._renderService.dimensions.css.cell.height}px`;const r=null!==(i=e.options.x)&&void 0!==i?i:0;return r&&r>this._bufferService.cols&&(s.style.display="none"),this._refreshXPosition(e,s),s}_refreshStyle(e){const t=e.marker.line-this._bufferService.buffers.active.ydisp;if(t<0||t>=this._bufferService.rows)e.element&&(e.element.style.display="none",e.onRenderEmitter.fire(e.element));else{let i=this._decorationElements.get(e);i||(i=this._createElement(e),e.element=i,this._decorationElements.set(e,i),this._container.appendChild(i),e.onDispose((()=>{this._decorationElements.delete(e),i.remove()}))),i.style.top=t*this._renderService.dimensions.css.cell.height+"px",i.style.display=this._altBufferIsActive?"none":"block",e.onRenderEmitter.fire(i)}}_refreshXPosition(e,t=e.element){var i;if(!t)return;const s=null!==(i=e.options.x)&&void 0!==i?i:0;"right"===(e.options.anchor||"left")?t.style.right=s?s*this._renderService.dimensions.css.cell.width+"px":"":t.style.left=s?s*this._renderService.dimensions.css.cell.width+"px":""}_removeDecoration(e){var t;null===(t=this._decorationElements.get(e))||void 0===t||t.remove(),this._decorationElements.delete(e),e.dispose()}};t.BufferDecorationRenderer=c=s([r(1,h.IBufferService),r(2,h.IDecorationService),r(3,o.IRenderService)],c)},5871:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.ColorZoneStore=void 0,t.ColorZoneStore=class{constructor(){this._zones=[],this._zonePool=[],this._zonePoolIndex=0,this._linePadding={full:0,left:0,center:0,right:0}}get zones(){return this._zonePool.length=Math.min(this._zonePool.length,this._zones.length),this._zones}clear(){this._zones.length=0,this._zonePoolIndex=0}addDecoration(e){if(e.options.overviewRulerOptions){for(const t of this._zones)if(t.color===e.options.overviewRulerOptions.color&&t.position===e.options.overviewRulerOptions.position){if(this._lineIntersectsZone(t,e.marker.line))return;if(this._lineAdjacentToZone(t,e.marker.line,e.options.overviewRulerOptions.position))return void this._addLineToZone(t,e.marker.line)}if(this._zonePoolIndex=e.startBufferLine&&t<=e.endBufferLine}_lineAdjacentToZone(e,t,i){return t>=e.startBufferLine-this._linePadding[i||"full"]&&t<=e.endBufferLine+this._linePadding[i||"full"]}_addLineToZone(e,t){e.startBufferLine=Math.min(e.startBufferLine,t),e.endBufferLine=Math.max(e.endBufferLine,t)}}},5744:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.OverviewRulerRenderer=void 0;const n=i(5871),o=i(3656),a=i(4725),h=i(844),c=i(2585),l={full:0,left:0,center:0,right:0},d={full:0,left:0,center:0,right:0},_={full:0,left:0,center:0,right:0};let u=t.OverviewRulerRenderer=class extends h.Disposable{get _width(){return this._optionsService.options.overviewRulerWidth||0}constructor(e,t,i,s,r,o,a){var c;super(),this._viewportElement=e,this._screenElement=t,this._bufferService=i,this._decorationService=s,this._renderService=r,this._optionsService=o,this._coreBrowseService=a,this._colorZoneStore=new n.ColorZoneStore,this._shouldUpdateDimensions=!0,this._shouldUpdateAnchor=!0,this._lastKnownBufferLength=0,this._canvas=document.createElement("canvas"),this._canvas.classList.add("xterm-decoration-overview-ruler"),this._refreshCanvasDimensions(),null===(c=this._viewportElement.parentElement)||void 0===c||c.insertBefore(this._canvas,this._viewportElement);const l=this._canvas.getContext("2d");if(!l)throw new Error("Ctx cannot be null");this._ctx=l,this._registerDecorationListeners(),this._registerBufferChangeListeners(),this._registerDimensionChangeListeners(),this.register((0,h.toDisposable)((()=>{var e;null===(e=this._canvas)||void 0===e||e.remove()})))}_registerDecorationListeners(){this.register(this._decorationService.onDecorationRegistered((()=>this._queueRefresh(void 0,!0)))),this.register(this._decorationService.onDecorationRemoved((()=>this._queueRefresh(void 0,!0))))}_registerBufferChangeListeners(){this.register(this._renderService.onRenderedViewportChange((()=>this._queueRefresh()))),this.register(this._bufferService.buffers.onBufferActivate((()=>{this._canvas.style.display=this._bufferService.buffer===this._bufferService.buffers.alt?"none":"block"}))),this.register(this._bufferService.onScroll((()=>{this._lastKnownBufferLength!==this._bufferService.buffers.normal.lines.length&&(this._refreshDrawHeightConstants(),this._refreshColorZonePadding())})))}_registerDimensionChangeListeners(){this.register(this._renderService.onRender((()=>{this._containerHeight&&this._containerHeight===this._screenElement.clientHeight||(this._queueRefresh(!0),this._containerHeight=this._screenElement.clientHeight)}))),this.register(this._optionsService.onSpecificOptionChange("overviewRulerWidth",(()=>this._queueRefresh(!0)))),this.register((0,o.addDisposableDomListener)(this._coreBrowseService.window,"resize",(()=>this._queueRefresh(!0)))),this._queueRefresh(!0)}_refreshDrawConstants(){const e=Math.floor(this._canvas.width/3),t=Math.ceil(this._canvas.width/3);d.full=this._canvas.width,d.left=e,d.center=t,d.right=e,this._refreshDrawHeightConstants(),_.full=0,_.left=0,_.center=d.left,_.right=d.left+d.center}_refreshDrawHeightConstants(){l.full=Math.round(2*this._coreBrowseService.dpr);const e=this._canvas.height/this._bufferService.buffer.lines.length,t=Math.round(Math.max(Math.min(e,12),6)*this._coreBrowseService.dpr);l.left=t,l.center=t,l.right=t}_refreshColorZonePadding(){this._colorZoneStore.setPadding({full:Math.floor(this._bufferService.buffers.active.lines.length/(this._canvas.height-1)*l.full),left:Math.floor(this._bufferService.buffers.active.lines.length/(this._canvas.height-1)*l.left),center:Math.floor(this._bufferService.buffers.active.lines.length/(this._canvas.height-1)*l.center),right:Math.floor(this._bufferService.buffers.active.lines.length/(this._canvas.height-1)*l.right)}),this._lastKnownBufferLength=this._bufferService.buffers.normal.lines.length}_refreshCanvasDimensions(){this._canvas.style.width=`${this._width}px`,this._canvas.width=Math.round(this._width*this._coreBrowseService.dpr),this._canvas.style.height=`${this._screenElement.clientHeight}px`,this._canvas.height=Math.round(this._screenElement.clientHeight*this._coreBrowseService.dpr),this._refreshDrawConstants(),this._refreshColorZonePadding()}_refreshDecorations(){this._shouldUpdateDimensions&&this._refreshCanvasDimensions(),this._ctx.clearRect(0,0,this._canvas.width,this._canvas.height),this._colorZoneStore.clear();for(const e of this._decorationService.decorations)this._colorZoneStore.addDecoration(e);this._ctx.lineWidth=1;const e=this._colorZoneStore.zones;for(const t of e)"full"!==t.position&&this._renderColorZone(t);for(const t of e)"full"===t.position&&this._renderColorZone(t);this._shouldUpdateDimensions=!1,this._shouldUpdateAnchor=!1}_renderColorZone(e){this._ctx.fillStyle=e.color,this._ctx.fillRect(_[e.position||"full"],Math.round((this._canvas.height-1)*(e.startBufferLine/this._bufferService.buffers.active.lines.length)-l[e.position||"full"]/2),d[e.position||"full"],Math.round((this._canvas.height-1)*((e.endBufferLine-e.startBufferLine)/this._bufferService.buffers.active.lines.length)+l[e.position||"full"]))}_queueRefresh(e,t){this._shouldUpdateDimensions=e||this._shouldUpdateDimensions,this._shouldUpdateAnchor=t||this._shouldUpdateAnchor,void 0===this._animationFrame&&(this._animationFrame=this._coreBrowseService.window.requestAnimationFrame((()=>{this._refreshDecorations(),this._animationFrame=void 0})))}};t.OverviewRulerRenderer=u=s([r(2,c.IBufferService),r(3,c.IDecorationService),r(4,a.IRenderService),r(5,c.IOptionsService),r(6,a.ICoreBrowserService)],u)},2950:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.CompositionHelper=void 0;const n=i(4725),o=i(2585),a=i(2584);let h=t.CompositionHelper=class{get isComposing(){return this._isComposing}constructor(e,t,i,s,r,n){this._textarea=e,this._compositionView=t,this._bufferService=i,this._optionsService=s,this._coreService=r,this._renderService=n,this._isComposing=!1,this._isSendingComposition=!1,this._compositionPosition={start:0,end:0},this._dataAlreadySent=""}compositionstart(){this._isComposing=!0,this._compositionPosition.start=this._textarea.value.length,this._compositionView.textContent="",this._dataAlreadySent="",this._compositionView.classList.add("active")}compositionupdate(e){this._compositionView.textContent=e.data,this.updateCompositionElements(),setTimeout((()=>{this._compositionPosition.end=this._textarea.value.length}),0)}compositionend(){this._finalizeComposition(!0)}keydown(e){if(this._isComposing||this._isSendingComposition){if(229===e.keyCode)return!1;if(16===e.keyCode||17===e.keyCode||18===e.keyCode)return!1;this._finalizeComposition(!1)}return 229!==e.keyCode||(this._handleAnyTextareaChanges(),!1)}_finalizeComposition(e){if(this._compositionView.classList.remove("active"),this._isComposing=!1,e){const e={start:this._compositionPosition.start,end:this._compositionPosition.end};this._isSendingComposition=!0,setTimeout((()=>{if(this._isSendingComposition){let t;this._isSendingComposition=!1,e.start+=this._dataAlreadySent.length,t=this._isComposing?this._textarea.value.substring(e.start,e.end):this._textarea.value.substring(e.start),t.length>0&&this._coreService.triggerDataEvent(t,!0)}}),0)}else{this._isSendingComposition=!1;const e=this._textarea.value.substring(this._compositionPosition.start,this._compositionPosition.end);this._coreService.triggerDataEvent(e,!0)}}_handleAnyTextareaChanges(){const e=this._textarea.value;setTimeout((()=>{if(!this._isComposing){const t=this._textarea.value,i=t.replace(e,"");this._dataAlreadySent=i,t.length>e.length?this._coreService.triggerDataEvent(i,!0):t.lengththis.updateCompositionElements(!0)),0)}}};t.CompositionHelper=h=s([r(2,o.IBufferService),r(3,o.IOptionsService),r(4,o.ICoreService),r(5,n.IRenderService)],h)},9806:(e,t)=>{function i(e,t,i){const s=i.getBoundingClientRect(),r=e.getComputedStyle(i),n=parseInt(r.getPropertyValue("padding-left")),o=parseInt(r.getPropertyValue("padding-top"));return[t.clientX-s.left-n,t.clientY-s.top-o]}Object.defineProperty(t,"__esModule",{value:!0}),t.getCoords=t.getCoordsRelativeToElement=void 0,t.getCoordsRelativeToElement=i,t.getCoords=function(e,t,s,r,n,o,a,h,c){if(!o)return;const l=i(e,t,s);return l?(l[0]=Math.ceil((l[0]+(c?a/2:0))/a),l[1]=Math.ceil(l[1]/h),l[0]=Math.min(Math.max(l[0],1),r+(c?1:0)),l[1]=Math.min(Math.max(l[1],1),n),l):void 0}},9504:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.moveToCellSequence=void 0;const s=i(2584);function r(e,t,i,s){const r=e-n(e,i),a=t-n(t,i),l=Math.abs(r-a)-function(e,t,i){let s=0;const r=e-n(e,i),a=t-n(t,i);for(let n=0;n=0&&et?"A":"B"}function a(e,t,i,s,r,n){let o=e,a=t,h="";for(;o!==i||a!==s;)o+=r?1:-1,r&&o>n.cols-1?(h+=n.buffer.translateBufferLineToString(a,!1,e,o),o=0,e=0,a++):!r&&o<0&&(h+=n.buffer.translateBufferLineToString(a,!1,0,e+1),o=n.cols-1,e=o,a--);return h+n.buffer.translateBufferLineToString(a,!1,e,o)}function h(e,t){const i=t?"O":"[";return s.C0.ESC+i+e}function c(e,t){e=Math.floor(e);let i="";for(let s=0;s0?s-n(s,o):t;const _=s,u=function(e,t,i,s,o,a){let h;return h=r(i,s,o,a).length>0?s-n(s,o):t,e=i&&he?"D":"C",c(Math.abs(o-e),h(d,s));d=l>t?"D":"C";const _=Math.abs(l-t);return c(function(e,t){return t.cols-e}(l>t?e:o,i)+(_-1)*i.cols+1+((l>t?o:e)-1),h(d,s))}},1296:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.DomRenderer=void 0;const n=i(3787),o=i(2550),a=i(2223),h=i(6171),c=i(4725),l=i(8055),d=i(8460),_=i(844),u=i(2585),f="xterm-dom-renderer-owner-",v="xterm-rows",p="xterm-fg-",g="xterm-bg-",m="xterm-focus",S="xterm-selection";let C=1,b=t.DomRenderer=class extends _.Disposable{constructor(e,t,i,s,r,a,c,l,u,p){super(),this._element=e,this._screenElement=t,this._viewportElement=i,this._linkifier2=s,this._charSizeService=a,this._optionsService=c,this._bufferService=l,this._coreBrowserService=u,this._themeService=p,this._terminalClass=C++,this._rowElements=[],this.onRequestRedraw=this.register(new d.EventEmitter).event,this._rowContainer=document.createElement("div"),this._rowContainer.classList.add(v),this._rowContainer.style.lineHeight="normal",this._rowContainer.setAttribute("aria-hidden","true"),this._refreshRowElements(this._bufferService.cols,this._bufferService.rows),this._selectionContainer=document.createElement("div"),this._selectionContainer.classList.add(S),this._selectionContainer.setAttribute("aria-hidden","true"),this.dimensions=(0,h.createRenderDimensions)(),this._updateDimensions(),this.register(this._optionsService.onOptionChange((()=>this._handleOptionsChanged()))),this.register(this._themeService.onChangeColors((e=>this._injectCss(e)))),this._injectCss(this._themeService.colors),this._rowFactory=r.createInstance(n.DomRendererRowFactory,document),this._element.classList.add(f+this._terminalClass),this._screenElement.appendChild(this._rowContainer),this._screenElement.appendChild(this._selectionContainer),this.register(this._linkifier2.onShowLinkUnderline((e=>this._handleLinkHover(e)))),this.register(this._linkifier2.onHideLinkUnderline((e=>this._handleLinkLeave(e)))),this.register((0,_.toDisposable)((()=>{this._element.classList.remove(f+this._terminalClass),this._rowContainer.remove(),this._selectionContainer.remove(),this._widthCache.dispose(),this._themeStyleElement.remove(),this._dimensionsStyleElement.remove()}))),this._widthCache=new o.WidthCache(document),this._widthCache.setFont(this._optionsService.rawOptions.fontFamily,this._optionsService.rawOptions.fontSize,this._optionsService.rawOptions.fontWeight,this._optionsService.rawOptions.fontWeightBold),this._setDefaultSpacing()}_updateDimensions(){const e=this._coreBrowserService.dpr;this.dimensions.device.char.width=this._charSizeService.width*e,this.dimensions.device.char.height=Math.ceil(this._charSizeService.height*e),this.dimensions.device.cell.width=this.dimensions.device.char.width+Math.round(this._optionsService.rawOptions.letterSpacing),this.dimensions.device.cell.height=Math.floor(this.dimensions.device.char.height*this._optionsService.rawOptions.lineHeight),this.dimensions.device.char.left=0,this.dimensions.device.char.top=0,this.dimensions.device.canvas.width=this.dimensions.device.cell.width*this._bufferService.cols,this.dimensions.device.canvas.height=this.dimensions.device.cell.height*this._bufferService.rows,this.dimensions.css.canvas.width=Math.round(this.dimensions.device.canvas.width/e),this.dimensions.css.canvas.height=Math.round(this.dimensions.device.canvas.height/e),this.dimensions.css.cell.width=this.dimensions.css.canvas.width/this._bufferService.cols,this.dimensions.css.cell.height=this.dimensions.css.canvas.height/this._bufferService.rows;for(const e of this._rowElements)e.style.width=`${this.dimensions.css.canvas.width}px`,e.style.height=`${this.dimensions.css.cell.height}px`,e.style.lineHeight=`${this.dimensions.css.cell.height}px`,e.style.overflow="hidden";this._dimensionsStyleElement||(this._dimensionsStyleElement=document.createElement("style"),this._screenElement.appendChild(this._dimensionsStyleElement));const t=`${this._terminalSelector} .${v} span { display: inline-block; height: 100%; vertical-align: top;}`;this._dimensionsStyleElement.textContent=t,this._selectionContainer.style.height=this._viewportElement.style.height,this._screenElement.style.width=`${this.dimensions.css.canvas.width}px`,this._screenElement.style.height=`${this.dimensions.css.canvas.height}px`}_injectCss(e){this._themeStyleElement||(this._themeStyleElement=document.createElement("style"),this._screenElement.appendChild(this._themeStyleElement));let t=`${this._terminalSelector} .${v} { color: ${e.foreground.css}; font-family: ${this._optionsService.rawOptions.fontFamily}; font-size: ${this._optionsService.rawOptions.fontSize}px; font-kerning: none; white-space: pre}`;t+=`${this._terminalSelector} .${v} .xterm-dim { color: ${l.color.multiplyOpacity(e.foreground,.5).css};}`,t+=`${this._terminalSelector} span:not(.xterm-bold) { font-weight: ${this._optionsService.rawOptions.fontWeight};}${this._terminalSelector} span.xterm-bold { font-weight: ${this._optionsService.rawOptions.fontWeightBold};}${this._terminalSelector} span.xterm-italic { font-style: italic;}`,t+="@keyframes blink_box_shadow_"+this._terminalClass+" { 50% { border-bottom-style: hidden; }}",t+="@keyframes blink_block_"+this._terminalClass+" { 0% {"+` background-color: ${e.cursor.css};`+` color: ${e.cursorAccent.css}; } 50% { background-color: inherit;`+` color: ${e.cursor.css}; }}`,t+=`${this._terminalSelector} .${v}.${m} .xterm-cursor.xterm-cursor-blink:not(.xterm-cursor-block) { animation: blink_box_shadow_`+this._terminalClass+" 1s step-end infinite;}"+`${this._terminalSelector} .${v}.${m} .xterm-cursor.xterm-cursor-blink.xterm-cursor-block { animation: blink_block_`+this._terminalClass+" 1s step-end infinite;}"+`${this._terminalSelector} .${v} .xterm-cursor.xterm-cursor-block {`+` background-color: ${e.cursor.css};`+` color: ${e.cursorAccent.css};}`+`${this._terminalSelector} .${v} .xterm-cursor.xterm-cursor-outline {`+` outline: 1px solid ${e.cursor.css}; outline-offset: -1px;}`+`${this._terminalSelector} .${v} .xterm-cursor.xterm-cursor-bar {`+` box-shadow: ${this._optionsService.rawOptions.cursorWidth}px 0 0 ${e.cursor.css} inset;}`+`${this._terminalSelector} .${v} .xterm-cursor.xterm-cursor-underline {`+` border-bottom: 1px ${e.cursor.css}; border-bottom-style: solid; height: calc(100% - 1px);}`,t+=`${this._terminalSelector} .${S} { position: absolute; top: 0; left: 0; z-index: 1; pointer-events: none;}${this._terminalSelector}.focus .${S} div { position: absolute; background-color: ${e.selectionBackgroundOpaque.css};}${this._terminalSelector} .${S} div { position: absolute; background-color: ${e.selectionInactiveBackgroundOpaque.css};}`;for(const[i,s]of e.ansi.entries())t+=`${this._terminalSelector} .${p}${i} { color: ${s.css}; }${this._terminalSelector} .${p}${i}.xterm-dim { color: ${l.color.multiplyOpacity(s,.5).css}; }${this._terminalSelector} .${g}${i} { background-color: ${s.css}; }`;t+=`${this._terminalSelector} .${p}${a.INVERTED_DEFAULT_COLOR} { color: ${l.color.opaque(e.background).css}; }${this._terminalSelector} .${p}${a.INVERTED_DEFAULT_COLOR}.xterm-dim { color: ${l.color.multiplyOpacity(l.color.opaque(e.background),.5).css}; }${this._terminalSelector} .${g}${a.INVERTED_DEFAULT_COLOR} { background-color: ${e.foreground.css}; }`,this._themeStyleElement.textContent=t}_setDefaultSpacing(){const e=this.dimensions.css.cell.width-this._widthCache.get("W",!1,!1);this._rowContainer.style.letterSpacing=`${e}px`,this._rowFactory.defaultSpacing=e}handleDevicePixelRatioChange(){this._updateDimensions(),this._widthCache.clear(),this._setDefaultSpacing()}_refreshRowElements(e,t){for(let e=this._rowElements.length;e<=t;e++){const e=document.createElement("div");this._rowContainer.appendChild(e),this._rowElements.push(e)}for(;this._rowElements.length>t;)this._rowContainer.removeChild(this._rowElements.pop())}handleResize(e,t){this._refreshRowElements(e,t),this._updateDimensions()}handleCharSizeChanged(){this._updateDimensions(),this._widthCache.clear(),this._setDefaultSpacing()}handleBlur(){this._rowContainer.classList.remove(m)}handleFocus(){this._rowContainer.classList.add(m),this.renderRows(this._bufferService.buffer.y,this._bufferService.buffer.y)}handleSelectionChanged(e,t,i){if(this._selectionContainer.replaceChildren(),this._rowFactory.handleSelectionChanged(e,t,i),this.renderRows(0,this._bufferService.rows-1),!e||!t)return;const s=e[1]-this._bufferService.buffer.ydisp,r=t[1]-this._bufferService.buffer.ydisp,n=Math.max(s,0),o=Math.min(r,this._bufferService.rows-1);if(n>=this._bufferService.rows||o<0)return;const a=document.createDocumentFragment();if(i){const i=e[0]>t[0];a.appendChild(this._createSelectionElement(n,i?t[0]:e[0],i?e[0]:t[0],o-n+1))}else{const i=s===n?e[0]:0,h=n===r?t[0]:this._bufferService.cols;a.appendChild(this._createSelectionElement(n,i,h));const c=o-n-1;if(a.appendChild(this._createSelectionElement(n+1,0,this._bufferService.cols,c)),n!==o){const e=r===o?t[0]:this._bufferService.cols;a.appendChild(this._createSelectionElement(o,0,e))}}this._selectionContainer.appendChild(a)}_createSelectionElement(e,t,i,s=1){const r=document.createElement("div");return r.style.height=s*this.dimensions.css.cell.height+"px",r.style.top=e*this.dimensions.css.cell.height+"px",r.style.left=t*this.dimensions.css.cell.width+"px",r.style.width=this.dimensions.css.cell.width*(i-t)+"px",r}handleCursorMove(){}_handleOptionsChanged(){this._updateDimensions(),this._injectCss(this._themeService.colors),this._widthCache.setFont(this._optionsService.rawOptions.fontFamily,this._optionsService.rawOptions.fontSize,this._optionsService.rawOptions.fontWeight,this._optionsService.rawOptions.fontWeightBold),this._setDefaultSpacing()}clear(){for(const e of this._rowElements)e.replaceChildren()}renderRows(e,t){const i=this._bufferService.buffer,s=i.ybase+i.y,r=Math.min(i.x,this._bufferService.cols-1),n=this._optionsService.rawOptions.cursorBlink,o=this._optionsService.rawOptions.cursorStyle,a=this._optionsService.rawOptions.cursorInactiveStyle;for(let h=e;h<=t;h++){const e=h+i.ydisp,t=this._rowElements[h],c=i.lines.get(e);if(!t||!c)break;t.replaceChildren(...this._rowFactory.createRow(c,e,e===s,o,a,r,n,this.dimensions.css.cell.width,this._widthCache,-1,-1))}}get _terminalSelector(){return`.${f}${this._terminalClass}`}_handleLinkHover(e){this._setCellUnderline(e.x1,e.x2,e.y1,e.y2,e.cols,!0)}_handleLinkLeave(e){this._setCellUnderline(e.x1,e.x2,e.y1,e.y2,e.cols,!1)}_setCellUnderline(e,t,i,s,r,n){i<0&&(e=0),s<0&&(t=0);const o=this._bufferService.rows-1;i=Math.max(Math.min(i,o),0),s=Math.max(Math.min(s,o),0),r=Math.min(r,this._bufferService.cols);const a=this._bufferService.buffer,h=a.ybase+a.y,c=Math.min(a.x,r-1),l=this._optionsService.rawOptions.cursorBlink,d=this._optionsService.rawOptions.cursorStyle,_=this._optionsService.rawOptions.cursorInactiveStyle;for(let o=i;o<=s;++o){const u=o+a.ydisp,f=this._rowElements[o],v=a.lines.get(u);if(!f||!v)break;f.replaceChildren(...this._rowFactory.createRow(v,u,u===h,d,_,c,l,this.dimensions.css.cell.width,this._widthCache,n?o===i?e:0:-1,n?(o===s?t:r)-1:-1))}}};t.DomRenderer=b=s([r(4,u.IInstantiationService),r(5,c.ICharSizeService),r(6,u.IOptionsService),r(7,u.IBufferService),r(8,c.ICoreBrowserService),r(9,c.IThemeService)],b)},3787:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.DomRendererRowFactory=void 0;const n=i(2223),o=i(643),a=i(511),h=i(2585),c=i(8055),l=i(4725),d=i(4269),_=i(6171),u=i(3734);let f=t.DomRendererRowFactory=class{constructor(e,t,i,s,r,n,o){this._document=e,this._characterJoinerService=t,this._optionsService=i,this._coreBrowserService=s,this._coreService=r,this._decorationService=n,this._themeService=o,this._workCell=new a.CellData,this._columnSelectMode=!1,this.defaultSpacing=0}handleSelectionChanged(e,t,i){this._selectionStart=e,this._selectionEnd=t,this._columnSelectMode=i}createRow(e,t,i,s,r,a,h,l,_,f,p){const g=[],m=this._characterJoinerService.getJoinedCharacters(t),S=this._themeService.colors;let C,b=e.getNoBgTrimmedLength();i&&b0&&M===m[0][0]){O=!0;const t=m.shift();I=new d.JoinedCellData(this._workCell,e.translateToString(!0,t[0],t[1]),t[1]-t[0]),P=t[1]-1,b=I.getWidth()}const H=this._isCellInSelection(M,t),F=i&&M===a,W=T&&M>=f&&M<=p;let U=!1;this._decorationService.forEachDecorationAtCell(M,t,void 0,(e=>{U=!0}));let N=I.getChars()||o.WHITESPACE_CELL_CHAR;if(" "===N&&(I.isUnderline()||I.isOverline())&&(N=" "),A=b*l-_.get(N,I.isBold(),I.isItalic()),C){if(y&&(H&&x||!H&&!x&&I.bg===E)&&(H&&x&&S.selectionForeground||I.fg===k)&&I.extended.ext===L&&W===D&&A===R&&!F&&!O&&!U){w+=N,y++;continue}y&&(C.textContent=w),C=this._document.createElement("span"),y=0,w=""}else C=this._document.createElement("span");if(E=I.bg,k=I.fg,L=I.extended.ext,D=W,R=A,x=H,O&&a>=M&&a<=P&&(a=M),!this._coreService.isCursorHidden&&F)if(B.push("xterm-cursor"),this._coreBrowserService.isFocused)h&&B.push("xterm-cursor-blink"),B.push("bar"===s?"xterm-cursor-bar":"underline"===s?"xterm-cursor-underline":"xterm-cursor-block");else if(r)switch(r){case"outline":B.push("xterm-cursor-outline");break;case"block":B.push("xterm-cursor-block");break;case"bar":B.push("xterm-cursor-bar");break;case"underline":B.push("xterm-cursor-underline")}if(I.isBold()&&B.push("xterm-bold"),I.isItalic()&&B.push("xterm-italic"),I.isDim()&&B.push("xterm-dim"),w=I.isInvisible()?o.WHITESPACE_CELL_CHAR:I.getChars()||o.WHITESPACE_CELL_CHAR,I.isUnderline()&&(B.push(`xterm-underline-${I.extended.underlineStyle}`)," "===w&&(w=" "),!I.isUnderlineColorDefault()))if(I.isUnderlineColorRGB())C.style.textDecorationColor=`rgb(${u.AttributeData.toColorRGB(I.getUnderlineColor()).join(",")})`;else{let e=I.getUnderlineColor();this._optionsService.rawOptions.drawBoldTextInBrightColors&&I.isBold()&&e<8&&(e+=8),C.style.textDecorationColor=S.ansi[e].css}I.isOverline()&&(B.push("xterm-overline")," "===w&&(w=" ")),I.isStrikethrough()&&B.push("xterm-strikethrough"),W&&(C.style.textDecoration="underline");let $=I.getFgColor(),j=I.getFgColorMode(),z=I.getBgColor(),K=I.getBgColorMode();const q=!!I.isInverse();if(q){const e=$;$=z,z=e;const t=j;j=K,K=t}let V,G,X,J=!1;switch(this._decorationService.forEachDecorationAtCell(M,t,void 0,(e=>{"top"!==e.options.layer&&J||(e.backgroundColorRGB&&(K=50331648,z=e.backgroundColorRGB.rgba>>8&16777215,V=e.backgroundColorRGB),e.foregroundColorRGB&&(j=50331648,$=e.foregroundColorRGB.rgba>>8&16777215,G=e.foregroundColorRGB),J="top"===e.options.layer)})),!J&&H&&(V=this._coreBrowserService.isFocused?S.selectionBackgroundOpaque:S.selectionInactiveBackgroundOpaque,z=V.rgba>>8&16777215,K=50331648,J=!0,S.selectionForeground&&(j=50331648,$=S.selectionForeground.rgba>>8&16777215,G=S.selectionForeground)),J&&B.push("xterm-decoration-top"),K){case 16777216:case 33554432:X=S.ansi[z],B.push(`xterm-bg-${z}`);break;case 50331648:X=c.rgba.toColor(z>>16,z>>8&255,255&z),this._addStyle(C,`background-color:#${v((z>>>0).toString(16),"0",6)}`);break;default:q?(X=S.foreground,B.push(`xterm-bg-${n.INVERTED_DEFAULT_COLOR}`)):X=S.background}switch(V||I.isDim()&&(V=c.color.multiplyOpacity(X,.5)),j){case 16777216:case 33554432:I.isBold()&&$<8&&this._optionsService.rawOptions.drawBoldTextInBrightColors&&($+=8),this._applyMinimumContrast(C,X,S.ansi[$],I,V,void 0)||B.push(`xterm-fg-${$}`);break;case 50331648:const e=c.rgba.toColor($>>16&255,$>>8&255,255&$);this._applyMinimumContrast(C,X,e,I,V,G)||this._addStyle(C,`color:#${v($.toString(16),"0",6)}`);break;default:this._applyMinimumContrast(C,X,S.foreground,I,V,void 0)||q&&B.push(`xterm-fg-${n.INVERTED_DEFAULT_COLOR}`)}B.length&&(C.className=B.join(" "),B.length=0),F||O||U?C.textContent=w:y++,A!==this.defaultSpacing&&(C.style.letterSpacing=`${A}px`),g.push(C),M=P}return C&&y&&(C.textContent=w),g}_applyMinimumContrast(e,t,i,s,r,n){if(1===this._optionsService.rawOptions.minimumContrastRatio||(0,_.excludeFromContrastRatioDemands)(s.getCode()))return!1;const o=this._getContrastCache(s);let a;if(r||n||(a=o.getColor(t.rgba,i.rgba)),void 0===a){const e=this._optionsService.rawOptions.minimumContrastRatio/(s.isDim()?2:1);a=c.color.ensureContrastRatio(r||t,n||i,e),o.setColor((r||t).rgba,(n||i).rgba,null!=a?a:null)}return!!a&&(this._addStyle(e,`color:${a.css}`),!0)}_getContrastCache(e){return e.isDim()?this._themeService.colors.halfContrastCache:this._themeService.colors.contrastCache}_addStyle(e,t){e.setAttribute("style",`${e.getAttribute("style")||""}${t};`)}_isCellInSelection(e,t){const i=this._selectionStart,s=this._selectionEnd;return!(!i||!s)&&(this._columnSelectMode?i[0]<=s[0]?e>=i[0]&&t>=i[1]&&e=i[1]&&e>=s[0]&&t<=s[1]:t>i[1]&&t=i[0]&&e=i[0])}};function v(e,t,i){for(;e.length{Object.defineProperty(t,"__esModule",{value:!0}),t.WidthCache=void 0,t.WidthCache=class{constructor(e){this._flat=new Float32Array(256),this._font="",this._fontSize=0,this._weight="normal",this._weightBold="bold",this._measureElements=[],this._container=e.createElement("div"),this._container.style.position="absolute",this._container.style.top="-50000px",this._container.style.width="50000px",this._container.style.whiteSpace="pre",this._container.style.fontKerning="none";const t=e.createElement("span"),i=e.createElement("span");i.style.fontWeight="bold";const s=e.createElement("span");s.style.fontStyle="italic";const r=e.createElement("span");r.style.fontWeight="bold",r.style.fontStyle="italic",this._measureElements=[t,i,s,r],this._container.appendChild(t),this._container.appendChild(i),this._container.appendChild(s),this._container.appendChild(r),e.body.appendChild(this._container),this.clear()}dispose(){this._container.remove(),this._measureElements.length=0,this._holey=void 0}clear(){this._flat.fill(-9999),this._holey=new Map}setFont(e,t,i,s){e===this._font&&t===this._fontSize&&i===this._weight&&s===this._weightBold||(this._font=e,this._fontSize=t,this._weight=i,this._weightBold=s,this._container.style.fontFamily=this._font,this._container.style.fontSize=`${this._fontSize}px`,this._measureElements[0].style.fontWeight=`${i}`,this._measureElements[1].style.fontWeight=`${s}`,this._measureElements[2].style.fontWeight=`${i}`,this._measureElements[3].style.fontWeight=`${s}`,this.clear())}get(e,t,i){let s=0;if(!t&&!i&&1===e.length&&(s=e.charCodeAt(0))<256)return-9999!==this._flat[s]?this._flat[s]:this._flat[s]=this._measure(e,0);let r=e;t&&(r+="B"),i&&(r+="I");let n=this._holey.get(r);if(void 0===n){let s=0;t&&(s|=1),i&&(s|=2),n=this._measure(e,s),this._holey.set(r,n)}return n}_measure(e,t){const i=this._measureElements[t];return i.textContent=e.repeat(32),i.offsetWidth/32}}},2223:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.TEXT_BASELINE=t.DIM_OPACITY=t.INVERTED_DEFAULT_COLOR=void 0;const s=i(6114);t.INVERTED_DEFAULT_COLOR=257,t.DIM_OPACITY=.5,t.TEXT_BASELINE=s.isFirefox||s.isLegacyEdge?"bottom":"ideographic"},6171:(e,t)=>{function i(e){return 57508<=e&&e<=57558}Object.defineProperty(t,"__esModule",{value:!0}),t.createRenderDimensions=t.excludeFromContrastRatioDemands=t.isRestrictedPowerlineGlyph=t.isPowerlineGlyph=t.throwIfFalsy=void 0,t.throwIfFalsy=function(e){if(!e)throw new Error("value must not be falsy");return e},t.isPowerlineGlyph=i,t.isRestrictedPowerlineGlyph=function(e){return 57520<=e&&e<=57527},t.excludeFromContrastRatioDemands=function(e){return i(e)||function(e){return 9472<=e&&e<=9631}(e)},t.createRenderDimensions=function(){return{css:{canvas:{width:0,height:0},cell:{width:0,height:0}},device:{canvas:{width:0,height:0},cell:{width:0,height:0},char:{width:0,height:0,left:0,top:0}}}}},456:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.SelectionModel=void 0,t.SelectionModel=class{constructor(e){this._bufferService=e,this.isSelectAllActive=!1,this.selectionStartLength=0}clearSelection(){this.selectionStart=void 0,this.selectionEnd=void 0,this.isSelectAllActive=!1,this.selectionStartLength=0}get finalSelectionStart(){return this.isSelectAllActive?[0,0]:this.selectionEnd&&this.selectionStart&&this.areSelectionValuesReversed()?this.selectionEnd:this.selectionStart}get finalSelectionEnd(){if(this.isSelectAllActive)return[this._bufferService.cols,this._bufferService.buffer.ybase+this._bufferService.rows-1];if(this.selectionStart){if(!this.selectionEnd||this.areSelectionValuesReversed()){const e=this.selectionStart[0]+this.selectionStartLength;return e>this._bufferService.cols?e%this._bufferService.cols==0?[this._bufferService.cols,this.selectionStart[1]+Math.floor(e/this._bufferService.cols)-1]:[e%this._bufferService.cols,this.selectionStart[1]+Math.floor(e/this._bufferService.cols)]:[e,this.selectionStart[1]]}if(this.selectionStartLength&&this.selectionEnd[1]===this.selectionStart[1]){const e=this.selectionStart[0]+this.selectionStartLength;return e>this._bufferService.cols?[e%this._bufferService.cols,this.selectionStart[1]+Math.floor(e/this._bufferService.cols)]:[Math.max(e,this.selectionEnd[0]),this.selectionEnd[1]]}return this.selectionEnd}}areSelectionValuesReversed(){const e=this.selectionStart,t=this.selectionEnd;return!(!e||!t)&&(e[1]>t[1]||e[1]===t[1]&&e[0]>t[0])}handleTrim(e){return this.selectionStart&&(this.selectionStart[1]-=e),this.selectionEnd&&(this.selectionEnd[1]-=e),this.selectionEnd&&this.selectionEnd[1]<0?(this.clearSelection(),!0):(this.selectionStart&&this.selectionStart[1]<0&&(this.selectionStart[1]=0),!1)}}},428:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.CharSizeService=void 0;const n=i(2585),o=i(8460),a=i(844);let h=t.CharSizeService=class extends a.Disposable{get hasValidSize(){return this.width>0&&this.height>0}constructor(e,t,i){super(),this._optionsService=i,this.width=0,this.height=0,this._onCharSizeChange=this.register(new o.EventEmitter),this.onCharSizeChange=this._onCharSizeChange.event,this._measureStrategy=new c(e,t,this._optionsService),this.register(this._optionsService.onMultipleOptionChange(["fontFamily","fontSize"],(()=>this.measure())))}measure(){const e=this._measureStrategy.measure();e.width===this.width&&e.height===this.height||(this.width=e.width,this.height=e.height,this._onCharSizeChange.fire())}};t.CharSizeService=h=s([r(2,n.IOptionsService)],h);class c{constructor(e,t,i){this._document=e,this._parentElement=t,this._optionsService=i,this._result={width:0,height:0},this._measureElement=this._document.createElement("span"),this._measureElement.classList.add("xterm-char-measure-element"),this._measureElement.textContent="W".repeat(32),this._measureElement.setAttribute("aria-hidden","true"),this._measureElement.style.whiteSpace="pre",this._measureElement.style.fontKerning="none",this._parentElement.appendChild(this._measureElement)}measure(){this._measureElement.style.fontFamily=this._optionsService.rawOptions.fontFamily,this._measureElement.style.fontSize=`${this._optionsService.rawOptions.fontSize}px`;const e={height:Number(this._measureElement.offsetHeight),width:Number(this._measureElement.offsetWidth)};return 0!==e.width&&0!==e.height&&(this._result.width=e.width/32,this._result.height=Math.ceil(e.height)),this._result}}},4269:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.CharacterJoinerService=t.JoinedCellData=void 0;const n=i(3734),o=i(643),a=i(511),h=i(2585);class c extends n.AttributeData{constructor(e,t,i){super(),this.content=0,this.combinedData="",this.fg=e.fg,this.bg=e.bg,this.combinedData=t,this._width=i}isCombined(){return 2097152}getWidth(){return this._width}getChars(){return this.combinedData}getCode(){return 2097151}setFromCharData(e){throw new Error("not implemented")}getAsCharData(){return[this.fg,this.getChars(),this.getWidth(),this.getCode()]}}t.JoinedCellData=c;let l=t.CharacterJoinerService=class e{constructor(e){this._bufferService=e,this._characterJoiners=[],this._nextCharacterJoinerId=0,this._workCell=new a.CellData}register(e){const t={id:this._nextCharacterJoinerId++,handler:e};return this._characterJoiners.push(t),t.id}deregister(e){for(let t=0;t1){const e=this._getJoinedRanges(s,a,n,t,r);for(let t=0;t1){const e=this._getJoinedRanges(s,a,n,t,r);for(let t=0;t{Object.defineProperty(t,"__esModule",{value:!0}),t.CoreBrowserService=void 0,t.CoreBrowserService=class{constructor(e,t){this._textarea=e,this.window=t,this._isFocused=!1,this._cachedIsFocused=void 0,this._textarea.addEventListener("focus",(()=>this._isFocused=!0)),this._textarea.addEventListener("blur",(()=>this._isFocused=!1))}get dpr(){return this.window.devicePixelRatio}get isFocused(){return void 0===this._cachedIsFocused&&(this._cachedIsFocused=this._isFocused&&this._textarea.ownerDocument.hasFocus(),queueMicrotask((()=>this._cachedIsFocused=void 0))),this._cachedIsFocused}}},8934:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.MouseService=void 0;const n=i(4725),o=i(9806);let a=t.MouseService=class{constructor(e,t){this._renderService=e,this._charSizeService=t}getCoords(e,t,i,s,r){return(0,o.getCoords)(window,e,t,i,s,this._charSizeService.hasValidSize,this._renderService.dimensions.css.cell.width,this._renderService.dimensions.css.cell.height,r)}getMouseReportCoords(e,t){const i=(0,o.getCoordsRelativeToElement)(window,e,t);if(this._charSizeService.hasValidSize)return i[0]=Math.min(Math.max(i[0],0),this._renderService.dimensions.css.canvas.width-1),i[1]=Math.min(Math.max(i[1],0),this._renderService.dimensions.css.canvas.height-1),{col:Math.floor(i[0]/this._renderService.dimensions.css.cell.width),row:Math.floor(i[1]/this._renderService.dimensions.css.cell.height),x:Math.floor(i[0]),y:Math.floor(i[1])}}};t.MouseService=a=s([r(0,n.IRenderService),r(1,n.ICharSizeService)],a)},3230:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.RenderService=void 0;const n=i(3656),o=i(6193),a=i(5596),h=i(4725),c=i(8460),l=i(844),d=i(7226),_=i(2585);let u=t.RenderService=class extends l.Disposable{get dimensions(){return this._renderer.value.dimensions}constructor(e,t,i,s,r,h,_,u){if(super(),this._rowCount=e,this._charSizeService=s,this._renderer=this.register(new l.MutableDisposable),this._pausedResizeTask=new d.DebouncedIdleTask,this._isPaused=!1,this._needsFullRefresh=!1,this._isNextRenderRedrawOnly=!0,this._needsSelectionRefresh=!1,this._canvasWidth=0,this._canvasHeight=0,this._selectionState={start:void 0,end:void 0,columnSelectMode:!1},this._onDimensionsChange=this.register(new c.EventEmitter),this.onDimensionsChange=this._onDimensionsChange.event,this._onRenderedViewportChange=this.register(new c.EventEmitter),this.onRenderedViewportChange=this._onRenderedViewportChange.event,this._onRender=this.register(new c.EventEmitter),this.onRender=this._onRender.event,this._onRefreshRequest=this.register(new c.EventEmitter),this.onRefreshRequest=this._onRefreshRequest.event,this._renderDebouncer=new o.RenderDebouncer(_.window,((e,t)=>this._renderRows(e,t))),this.register(this._renderDebouncer),this._screenDprMonitor=new a.ScreenDprMonitor(_.window),this._screenDprMonitor.setListener((()=>this.handleDevicePixelRatioChange())),this.register(this._screenDprMonitor),this.register(h.onResize((()=>this._fullRefresh()))),this.register(h.buffers.onBufferActivate((()=>{var e;return null===(e=this._renderer.value)||void 0===e?void 0:e.clear()}))),this.register(i.onOptionChange((()=>this._handleOptionsChanged()))),this.register(this._charSizeService.onCharSizeChange((()=>this.handleCharSizeChanged()))),this.register(r.onDecorationRegistered((()=>this._fullRefresh()))),this.register(r.onDecorationRemoved((()=>this._fullRefresh()))),this.register(i.onMultipleOptionChange(["customGlyphs","drawBoldTextInBrightColors","letterSpacing","lineHeight","fontFamily","fontSize","fontWeight","fontWeightBold","minimumContrastRatio"],(()=>{this.clear(),this.handleResize(h.cols,h.rows),this._fullRefresh()}))),this.register(i.onMultipleOptionChange(["cursorBlink","cursorStyle"],(()=>this.refreshRows(h.buffer.y,h.buffer.y,!0)))),this.register((0,n.addDisposableDomListener)(_.window,"resize",(()=>this.handleDevicePixelRatioChange()))),this.register(u.onChangeColors((()=>this._fullRefresh()))),"IntersectionObserver"in _.window){const e=new _.window.IntersectionObserver((e=>this._handleIntersectionChange(e[e.length-1])),{threshold:0});e.observe(t),this.register({dispose:()=>e.disconnect()})}}_handleIntersectionChange(e){this._isPaused=void 0===e.isIntersecting?0===e.intersectionRatio:!e.isIntersecting,this._isPaused||this._charSizeService.hasValidSize||this._charSizeService.measure(),!this._isPaused&&this._needsFullRefresh&&(this._pausedResizeTask.flush(),this.refreshRows(0,this._rowCount-1),this._needsFullRefresh=!1)}refreshRows(e,t,i=!1){this._isPaused?this._needsFullRefresh=!0:(i||(this._isNextRenderRedrawOnly=!1),this._renderDebouncer.refresh(e,t,this._rowCount))}_renderRows(e,t){this._renderer.value&&(e=Math.min(e,this._rowCount-1),t=Math.min(t,this._rowCount-1),this._renderer.value.renderRows(e,t),this._needsSelectionRefresh&&(this._renderer.value.handleSelectionChanged(this._selectionState.start,this._selectionState.end,this._selectionState.columnSelectMode),this._needsSelectionRefresh=!1),this._isNextRenderRedrawOnly||this._onRenderedViewportChange.fire({start:e,end:t}),this._onRender.fire({start:e,end:t}),this._isNextRenderRedrawOnly=!0)}resize(e,t){this._rowCount=t,this._fireOnCanvasResize()}_handleOptionsChanged(){this._renderer.value&&(this.refreshRows(0,this._rowCount-1),this._fireOnCanvasResize())}_fireOnCanvasResize(){this._renderer.value&&(this._renderer.value.dimensions.css.canvas.width===this._canvasWidth&&this._renderer.value.dimensions.css.canvas.height===this._canvasHeight||this._onDimensionsChange.fire(this._renderer.value.dimensions))}hasRenderer(){return!!this._renderer.value}setRenderer(e){this._renderer.value=e,this._renderer.value.onRequestRedraw((e=>this.refreshRows(e.start,e.end,!0))),this._needsSelectionRefresh=!0,this._fullRefresh()}addRefreshCallback(e){return this._renderDebouncer.addRefreshCallback(e)}_fullRefresh(){this._isPaused?this._needsFullRefresh=!0:this.refreshRows(0,this._rowCount-1)}clearTextureAtlas(){var e,t;this._renderer.value&&(null===(t=(e=this._renderer.value).clearTextureAtlas)||void 0===t||t.call(e),this._fullRefresh())}handleDevicePixelRatioChange(){this._charSizeService.measure(),this._renderer.value&&(this._renderer.value.handleDevicePixelRatioChange(),this.refreshRows(0,this._rowCount-1))}handleResize(e,t){this._renderer.value&&(this._isPaused?this._pausedResizeTask.set((()=>this._renderer.value.handleResize(e,t))):this._renderer.value.handleResize(e,t),this._fullRefresh())}handleCharSizeChanged(){var e;null===(e=this._renderer.value)||void 0===e||e.handleCharSizeChanged()}handleBlur(){var e;null===(e=this._renderer.value)||void 0===e||e.handleBlur()}handleFocus(){var e;null===(e=this._renderer.value)||void 0===e||e.handleFocus()}handleSelectionChanged(e,t,i){var s;this._selectionState.start=e,this._selectionState.end=t,this._selectionState.columnSelectMode=i,null===(s=this._renderer.value)||void 0===s||s.handleSelectionChanged(e,t,i)}handleCursorMove(){var e;null===(e=this._renderer.value)||void 0===e||e.handleCursorMove()}clear(){var e;null===(e=this._renderer.value)||void 0===e||e.clear()}};t.RenderService=u=s([r(2,_.IOptionsService),r(3,h.ICharSizeService),r(4,_.IDecorationService),r(5,_.IBufferService),r(6,h.ICoreBrowserService),r(7,h.IThemeService)],u)},9312:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.SelectionService=void 0;const n=i(9806),o=i(9504),a=i(456),h=i(4725),c=i(8460),l=i(844),d=i(6114),_=i(4841),u=i(511),f=i(2585),v=String.fromCharCode(160),p=new RegExp(v,"g");let g=t.SelectionService=class extends l.Disposable{constructor(e,t,i,s,r,n,o,h,d){super(),this._element=e,this._screenElement=t,this._linkifier=i,this._bufferService=s,this._coreService=r,this._mouseService=n,this._optionsService=o,this._renderService=h,this._coreBrowserService=d,this._dragScrollAmount=0,this._enabled=!0,this._workCell=new u.CellData,this._mouseDownTimeStamp=0,this._oldHasSelection=!1,this._oldSelectionStart=void 0,this._oldSelectionEnd=void 0,this._onLinuxMouseSelection=this.register(new c.EventEmitter),this.onLinuxMouseSelection=this._onLinuxMouseSelection.event,this._onRedrawRequest=this.register(new c.EventEmitter),this.onRequestRedraw=this._onRedrawRequest.event,this._onSelectionChange=this.register(new c.EventEmitter),this.onSelectionChange=this._onSelectionChange.event,this._onRequestScrollLines=this.register(new c.EventEmitter),this.onRequestScrollLines=this._onRequestScrollLines.event,this._mouseMoveListener=e=>this._handleMouseMove(e),this._mouseUpListener=e=>this._handleMouseUp(e),this._coreService.onUserInput((()=>{this.hasSelection&&this.clearSelection()})),this._trimListener=this._bufferService.buffer.lines.onTrim((e=>this._handleTrim(e))),this.register(this._bufferService.buffers.onBufferActivate((e=>this._handleBufferActivate(e)))),this.enable(),this._model=new a.SelectionModel(this._bufferService),this._activeSelectionMode=0,this.register((0,l.toDisposable)((()=>{this._removeMouseDownListeners()})))}reset(){this.clearSelection()}disable(){this.clearSelection(),this._enabled=!1}enable(){this._enabled=!0}get selectionStart(){return this._model.finalSelectionStart}get selectionEnd(){return this._model.finalSelectionEnd}get hasSelection(){const e=this._model.finalSelectionStart,t=this._model.finalSelectionEnd;return!(!e||!t||e[0]===t[0]&&e[1]===t[1])}get selectionText(){const e=this._model.finalSelectionStart,t=this._model.finalSelectionEnd;if(!e||!t)return"";const i=this._bufferService.buffer,s=[];if(3===this._activeSelectionMode){if(e[0]===t[0])return"";const r=e[0]e.replace(p," "))).join(d.isWindows?"\r\n":"\n")}clearSelection(){this._model.clearSelection(),this._removeMouseDownListeners(),this.refresh(),this._onSelectionChange.fire()}refresh(e){this._refreshAnimationFrame||(this._refreshAnimationFrame=this._coreBrowserService.window.requestAnimationFrame((()=>this._refresh()))),d.isLinux&&e&&this.selectionText.length&&this._onLinuxMouseSelection.fire(this.selectionText)}_refresh(){this._refreshAnimationFrame=void 0,this._onRedrawRequest.fire({start:this._model.finalSelectionStart,end:this._model.finalSelectionEnd,columnSelectMode:3===this._activeSelectionMode})}_isClickInSelection(e){const t=this._getMouseBufferCoords(e),i=this._model.finalSelectionStart,s=this._model.finalSelectionEnd;return!!(i&&s&&t)&&this._areCoordsInSelection(t,i,s)}isCellInSelection(e,t){const i=this._model.finalSelectionStart,s=this._model.finalSelectionEnd;return!(!i||!s)&&this._areCoordsInSelection([e,t],i,s)}_areCoordsInSelection(e,t,i){return e[1]>t[1]&&e[1]=t[0]&&e[0]=t[0]}_selectWordAtCursor(e,t){var i,s;const r=null===(s=null===(i=this._linkifier.currentLink)||void 0===i?void 0:i.link)||void 0===s?void 0:s.range;if(r)return this._model.selectionStart=[r.start.x-1,r.start.y-1],this._model.selectionStartLength=(0,_.getRangeLength)(r,this._bufferService.cols),this._model.selectionEnd=void 0,!0;const n=this._getMouseBufferCoords(e);return!!n&&(this._selectWordAt(n,t),this._model.selectionEnd=void 0,!0)}selectAll(){this._model.isSelectAllActive=!0,this.refresh(),this._onSelectionChange.fire()}selectLines(e,t){this._model.clearSelection(),e=Math.max(e,0),t=Math.min(t,this._bufferService.buffer.lines.length-1),this._model.selectionStart=[0,e],this._model.selectionEnd=[this._bufferService.cols,t],this.refresh(),this._onSelectionChange.fire()}_handleTrim(e){this._model.handleTrim(e)&&this.refresh()}_getMouseBufferCoords(e){const t=this._mouseService.getCoords(e,this._screenElement,this._bufferService.cols,this._bufferService.rows,!0);if(t)return t[0]--,t[1]--,t[1]+=this._bufferService.buffer.ydisp,t}_getMouseEventScrollAmount(e){let t=(0,n.getCoordsRelativeToElement)(this._coreBrowserService.window,e,this._screenElement)[1];const i=this._renderService.dimensions.css.canvas.height;return t>=0&&t<=i?0:(t>i&&(t-=i),t=Math.min(Math.max(t,-50),50),t/=50,t/Math.abs(t)+Math.round(14*t))}shouldForceSelection(e){return d.isMac?e.altKey&&this._optionsService.rawOptions.macOptionClickForcesSelection:e.shiftKey}handleMouseDown(e){if(this._mouseDownTimeStamp=e.timeStamp,(2!==e.button||!this.hasSelection)&&0===e.button){if(!this._enabled){if(!this.shouldForceSelection(e))return;e.stopPropagation()}e.preventDefault(),this._dragScrollAmount=0,this._enabled&&e.shiftKey?this._handleIncrementalClick(e):1===e.detail?this._handleSingleClick(e):2===e.detail?this._handleDoubleClick(e):3===e.detail&&this._handleTripleClick(e),this._addMouseDownListeners(),this.refresh(!0)}}_addMouseDownListeners(){this._screenElement.ownerDocument&&(this._screenElement.ownerDocument.addEventListener("mousemove",this._mouseMoveListener),this._screenElement.ownerDocument.addEventListener("mouseup",this._mouseUpListener)),this._dragScrollIntervalTimer=this._coreBrowserService.window.setInterval((()=>this._dragScroll()),50)}_removeMouseDownListeners(){this._screenElement.ownerDocument&&(this._screenElement.ownerDocument.removeEventListener("mousemove",this._mouseMoveListener),this._screenElement.ownerDocument.removeEventListener("mouseup",this._mouseUpListener)),this._coreBrowserService.window.clearInterval(this._dragScrollIntervalTimer),this._dragScrollIntervalTimer=void 0}_handleIncrementalClick(e){this._model.selectionStart&&(this._model.selectionEnd=this._getMouseBufferCoords(e))}_handleSingleClick(e){if(this._model.selectionStartLength=0,this._model.isSelectAllActive=!1,this._activeSelectionMode=this.shouldColumnSelect(e)?3:0,this._model.selectionStart=this._getMouseBufferCoords(e),!this._model.selectionStart)return;this._model.selectionEnd=void 0;const t=this._bufferService.buffer.lines.get(this._model.selectionStart[1]);t&&t.length!==this._model.selectionStart[0]&&0===t.hasWidth(this._model.selectionStart[0])&&this._model.selectionStart[0]++}_handleDoubleClick(e){this._selectWordAtCursor(e,!0)&&(this._activeSelectionMode=1)}_handleTripleClick(e){const t=this._getMouseBufferCoords(e);t&&(this._activeSelectionMode=2,this._selectLineAt(t[1]))}shouldColumnSelect(e){return e.altKey&&!(d.isMac&&this._optionsService.rawOptions.macOptionClickForcesSelection)}_handleMouseMove(e){if(e.stopImmediatePropagation(),!this._model.selectionStart)return;const t=this._model.selectionEnd?[this._model.selectionEnd[0],this._model.selectionEnd[1]]:null;if(this._model.selectionEnd=this._getMouseBufferCoords(e),!this._model.selectionEnd)return void this.refresh(!0);2===this._activeSelectionMode?this._model.selectionEnd[1]0?this._model.selectionEnd[0]=this._bufferService.cols:this._dragScrollAmount<0&&(this._model.selectionEnd[0]=0));const i=this._bufferService.buffer;if(this._model.selectionEnd[1]0?(3!==this._activeSelectionMode&&(this._model.selectionEnd[0]=this._bufferService.cols),this._model.selectionEnd[1]=Math.min(e.ydisp+this._bufferService.rows,e.lines.length-1)):(3!==this._activeSelectionMode&&(this._model.selectionEnd[0]=0),this._model.selectionEnd[1]=e.ydisp),this.refresh()}}_handleMouseUp(e){const t=e.timeStamp-this._mouseDownTimeStamp;if(this._removeMouseDownListeners(),this.selectionText.length<=1&&t<500&&e.altKey&&this._optionsService.rawOptions.altClickMovesCursor){if(this._bufferService.buffer.ybase===this._bufferService.buffer.ydisp){const t=this._mouseService.getCoords(e,this._element,this._bufferService.cols,this._bufferService.rows,!1);if(t&&void 0!==t[0]&&void 0!==t[1]){const e=(0,o.moveToCellSequence)(t[0]-1,t[1]-1,this._bufferService,this._coreService.decPrivateModes.applicationCursorKeys);this._coreService.triggerDataEvent(e,!0)}}}else this._fireEventIfSelectionChanged()}_fireEventIfSelectionChanged(){const e=this._model.finalSelectionStart,t=this._model.finalSelectionEnd,i=!(!e||!t||e[0]===t[0]&&e[1]===t[1]);i?e&&t&&(this._oldSelectionStart&&this._oldSelectionEnd&&e[0]===this._oldSelectionStart[0]&&e[1]===this._oldSelectionStart[1]&&t[0]===this._oldSelectionEnd[0]&&t[1]===this._oldSelectionEnd[1]||this._fireOnSelectionChange(e,t,i)):this._oldHasSelection&&this._fireOnSelectionChange(e,t,i)}_fireOnSelectionChange(e,t,i){this._oldSelectionStart=e,this._oldSelectionEnd=t,this._oldHasSelection=i,this._onSelectionChange.fire()}_handleBufferActivate(e){this.clearSelection(),this._trimListener.dispose(),this._trimListener=e.activeBuffer.lines.onTrim((e=>this._handleTrim(e)))}_convertViewportColToCharacterIndex(e,t){let i=t;for(let s=0;t>=s;s++){const r=e.loadCell(s,this._workCell).getChars().length;0===this._workCell.getWidth()?i--:r>1&&t!==s&&(i+=r-1)}return i}setSelection(e,t,i){this._model.clearSelection(),this._removeMouseDownListeners(),this._model.selectionStart=[e,t],this._model.selectionStartLength=i,this.refresh(),this._fireEventIfSelectionChanged()}rightClickSelect(e){this._isClickInSelection(e)||(this._selectWordAtCursor(e,!1)&&this.refresh(!0),this._fireEventIfSelectionChanged())}_getWordAt(e,t,i=!0,s=!0){if(e[0]>=this._bufferService.cols)return;const r=this._bufferService.buffer,n=r.lines.get(e[1]);if(!n)return;const o=r.translateBufferLineToString(e[1],!1);let a=this._convertViewportColToCharacterIndex(n,e[0]),h=a;const c=e[0]-a;let l=0,d=0,_=0,u=0;if(" "===o.charAt(a)){for(;a>0&&" "===o.charAt(a-1);)a--;for(;h1&&(u+=s-1,h+=s-1);t>0&&a>0&&!this._isCharWordSeparator(n.loadCell(t-1,this._workCell));){n.loadCell(t-1,this._workCell);const e=this._workCell.getChars().length;0===this._workCell.getWidth()?(l++,t--):e>1&&(_+=e-1,a-=e-1),a--,t--}for(;i1&&(u+=e-1,h+=e-1),h++,i++}}h++;let f=a+c-l+_,v=Math.min(this._bufferService.cols,h-a+l+d-_-u);if(t||""!==o.slice(a,h).trim()){if(i&&0===f&&32!==n.getCodePoint(0)){const t=r.lines.get(e[1]-1);if(t&&n.isWrapped&&32!==t.getCodePoint(this._bufferService.cols-1)){const t=this._getWordAt([this._bufferService.cols-1,e[1]-1],!1,!0,!1);if(t){const e=this._bufferService.cols-t.start;f-=e,v+=e}}}if(s&&f+v===this._bufferService.cols&&32!==n.getCodePoint(this._bufferService.cols-1)){const t=r.lines.get(e[1]+1);if((null==t?void 0:t.isWrapped)&&32!==t.getCodePoint(0)){const t=this._getWordAt([0,e[1]+1],!1,!1,!0);t&&(v+=t.length)}}return{start:f,length:v}}}_selectWordAt(e,t){const i=this._getWordAt(e,t);if(i){for(;i.start<0;)i.start+=this._bufferService.cols,e[1]--;this._model.selectionStart=[i.start,e[1]],this._model.selectionStartLength=i.length}}_selectToWordAt(e){const t=this._getWordAt(e,!0);if(t){let i=e[1];for(;t.start<0;)t.start+=this._bufferService.cols,i--;if(!this._model.areSelectionValuesReversed())for(;t.start+t.length>this._bufferService.cols;)t.length-=this._bufferService.cols,i++;this._model.selectionEnd=[this._model.areSelectionValuesReversed()?t.start:t.start+t.length,i]}}_isCharWordSeparator(e){return 0!==e.getWidth()&&this._optionsService.rawOptions.wordSeparator.indexOf(e.getChars())>=0}_selectLineAt(e){const t=this._bufferService.buffer.getWrappedRangeForLine(e),i={start:{x:0,y:t.first},end:{x:this._bufferService.cols-1,y:t.last}};this._model.selectionStart=[0,t.first],this._model.selectionEnd=void 0,this._model.selectionStartLength=(0,_.getRangeLength)(i,this._bufferService.cols)}};t.SelectionService=g=s([r(3,f.IBufferService),r(4,f.ICoreService),r(5,h.IMouseService),r(6,f.IOptionsService),r(7,h.IRenderService),r(8,h.ICoreBrowserService)],g)},4725:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.IThemeService=t.ICharacterJoinerService=t.ISelectionService=t.IRenderService=t.IMouseService=t.ICoreBrowserService=t.ICharSizeService=void 0;const s=i(8343);t.ICharSizeService=(0,s.createDecorator)("CharSizeService"),t.ICoreBrowserService=(0,s.createDecorator)("CoreBrowserService"),t.IMouseService=(0,s.createDecorator)("MouseService"),t.IRenderService=(0,s.createDecorator)("RenderService"),t.ISelectionService=(0,s.createDecorator)("SelectionService"),t.ICharacterJoinerService=(0,s.createDecorator)("CharacterJoinerService"),t.IThemeService=(0,s.createDecorator)("ThemeService")},6731:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.ThemeService=t.DEFAULT_ANSI_COLORS=void 0;const n=i(7239),o=i(8055),a=i(8460),h=i(844),c=i(2585),l=o.css.toColor("#ffffff"),d=o.css.toColor("#000000"),_=o.css.toColor("#ffffff"),u=o.css.toColor("#000000"),f={css:"rgba(255, 255, 255, 0.3)",rgba:4294967117};t.DEFAULT_ANSI_COLORS=Object.freeze((()=>{const e=[o.css.toColor("#2e3436"),o.css.toColor("#cc0000"),o.css.toColor("#4e9a06"),o.css.toColor("#c4a000"),o.css.toColor("#3465a4"),o.css.toColor("#75507b"),o.css.toColor("#06989a"),o.css.toColor("#d3d7cf"),o.css.toColor("#555753"),o.css.toColor("#ef2929"),o.css.toColor("#8ae234"),o.css.toColor("#fce94f"),o.css.toColor("#729fcf"),o.css.toColor("#ad7fa8"),o.css.toColor("#34e2e2"),o.css.toColor("#eeeeec")],t=[0,95,135,175,215,255];for(let i=0;i<216;i++){const s=t[i/36%6|0],r=t[i/6%6|0],n=t[i%6];e.push({css:o.channels.toCss(s,r,n),rgba:o.channels.toRgba(s,r,n)})}for(let t=0;t<24;t++){const i=8+10*t;e.push({css:o.channels.toCss(i,i,i),rgba:o.channels.toRgba(i,i,i)})}return e})());let v=t.ThemeService=class extends h.Disposable{get colors(){return this._colors}constructor(e){super(),this._optionsService=e,this._contrastCache=new n.ColorContrastCache,this._halfContrastCache=new n.ColorContrastCache,this._onChangeColors=this.register(new a.EventEmitter),this.onChangeColors=this._onChangeColors.event,this._colors={foreground:l,background:d,cursor:_,cursorAccent:u,selectionForeground:void 0,selectionBackgroundTransparent:f,selectionBackgroundOpaque:o.color.blend(d,f),selectionInactiveBackgroundTransparent:f,selectionInactiveBackgroundOpaque:o.color.blend(d,f),ansi:t.DEFAULT_ANSI_COLORS.slice(),contrastCache:this._contrastCache,halfContrastCache:this._halfContrastCache},this._updateRestoreColors(),this._setTheme(this._optionsService.rawOptions.theme),this.register(this._optionsService.onSpecificOptionChange("minimumContrastRatio",(()=>this._contrastCache.clear()))),this.register(this._optionsService.onSpecificOptionChange("theme",(()=>this._setTheme(this._optionsService.rawOptions.theme))))}_setTheme(e={}){const i=this._colors;if(i.foreground=p(e.foreground,l),i.background=p(e.background,d),i.cursor=p(e.cursor,_),i.cursorAccent=p(e.cursorAccent,u),i.selectionBackgroundTransparent=p(e.selectionBackground,f),i.selectionBackgroundOpaque=o.color.blend(i.background,i.selectionBackgroundTransparent),i.selectionInactiveBackgroundTransparent=p(e.selectionInactiveBackground,i.selectionBackgroundTransparent),i.selectionInactiveBackgroundOpaque=o.color.blend(i.background,i.selectionInactiveBackgroundTransparent),i.selectionForeground=e.selectionForeground?p(e.selectionForeground,o.NULL_COLOR):void 0,i.selectionForeground===o.NULL_COLOR&&(i.selectionForeground=void 0),o.color.isOpaque(i.selectionBackgroundTransparent)){const e=.3;i.selectionBackgroundTransparent=o.color.opacity(i.selectionBackgroundTransparent,e)}if(o.color.isOpaque(i.selectionInactiveBackgroundTransparent)){const e=.3;i.selectionInactiveBackgroundTransparent=o.color.opacity(i.selectionInactiveBackgroundTransparent,e)}if(i.ansi=t.DEFAULT_ANSI_COLORS.slice(),i.ansi[0]=p(e.black,t.DEFAULT_ANSI_COLORS[0]),i.ansi[1]=p(e.red,t.DEFAULT_ANSI_COLORS[1]),i.ansi[2]=p(e.green,t.DEFAULT_ANSI_COLORS[2]),i.ansi[3]=p(e.yellow,t.DEFAULT_ANSI_COLORS[3]),i.ansi[4]=p(e.blue,t.DEFAULT_ANSI_COLORS[4]),i.ansi[5]=p(e.magenta,t.DEFAULT_ANSI_COLORS[5]),i.ansi[6]=p(e.cyan,t.DEFAULT_ANSI_COLORS[6]),i.ansi[7]=p(e.white,t.DEFAULT_ANSI_COLORS[7]),i.ansi[8]=p(e.brightBlack,t.DEFAULT_ANSI_COLORS[8]),i.ansi[9]=p(e.brightRed,t.DEFAULT_ANSI_COLORS[9]),i.ansi[10]=p(e.brightGreen,t.DEFAULT_ANSI_COLORS[10]),i.ansi[11]=p(e.brightYellow,t.DEFAULT_ANSI_COLORS[11]),i.ansi[12]=p(e.brightBlue,t.DEFAULT_ANSI_COLORS[12]),i.ansi[13]=p(e.brightMagenta,t.DEFAULT_ANSI_COLORS[13]),i.ansi[14]=p(e.brightCyan,t.DEFAULT_ANSI_COLORS[14]),i.ansi[15]=p(e.brightWhite,t.DEFAULT_ANSI_COLORS[15]),e.extendedAnsi){const s=Math.min(i.ansi.length-16,e.extendedAnsi.length);for(let r=0;r{Object.defineProperty(t,"__esModule",{value:!0}),t.CircularList=void 0;const s=i(8460),r=i(844);class n extends r.Disposable{constructor(e){super(),this._maxLength=e,this.onDeleteEmitter=this.register(new s.EventEmitter),this.onDelete=this.onDeleteEmitter.event,this.onInsertEmitter=this.register(new s.EventEmitter),this.onInsert=this.onInsertEmitter.event,this.onTrimEmitter=this.register(new s.EventEmitter),this.onTrim=this.onTrimEmitter.event,this._array=new Array(this._maxLength),this._startIndex=0,this._length=0}get maxLength(){return this._maxLength}set maxLength(e){if(this._maxLength===e)return;const t=new Array(e);for(let i=0;ithis._length)for(let t=this._length;t=e;t--)this._array[this._getCyclicIndex(t+i.length)]=this._array[this._getCyclicIndex(t)];for(let t=0;tthis._maxLength){const e=this._length+i.length-this._maxLength;this._startIndex+=e,this._length=this._maxLength,this.onTrimEmitter.fire(e)}else this._length+=i.length}trimStart(e){e>this._length&&(e=this._length),this._startIndex+=e,this._length-=e,this.onTrimEmitter.fire(e)}shiftElements(e,t,i){if(!(t<=0)){if(e<0||e>=this._length)throw new Error("start argument out of range");if(e+i<0)throw new Error("Cannot shift elements in list beyond index 0");if(i>0){for(let s=t-1;s>=0;s--)this.set(e+s+i,this.get(e+s));const s=e+t+i-this._length;if(s>0)for(this._length+=s;this._length>this._maxLength;)this._length--,this._startIndex++,this.onTrimEmitter.fire(1)}else for(let s=0;s{Object.defineProperty(t,"__esModule",{value:!0}),t.clone=void 0,t.clone=function e(t,i=5){if("object"!=typeof t)return t;const s=Array.isArray(t)?[]:{};for(const r in t)s[r]=i<=1?t[r]:t[r]&&e(t[r],i-1);return s}},8055:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.contrastRatio=t.toPaddedHex=t.rgba=t.rgb=t.css=t.color=t.channels=t.NULL_COLOR=void 0;const s=i(6114);let r=0,n=0,o=0,a=0;var h,c,l,d,_;function u(e){const t=e.toString(16);return t.length<2?"0"+t:t}function f(e,t){return e>>0}}(h||(t.channels=h={})),function(e){function t(e,t){return a=Math.round(255*t),[r,n,o]=_.toChannels(e.rgba),{css:h.toCss(r,n,o,a),rgba:h.toRgba(r,n,o,a)}}e.blend=function(e,t){if(a=(255&t.rgba)/255,1===a)return{css:t.css,rgba:t.rgba};const i=t.rgba>>24&255,s=t.rgba>>16&255,c=t.rgba>>8&255,l=e.rgba>>24&255,d=e.rgba>>16&255,_=e.rgba>>8&255;return r=l+Math.round((i-l)*a),n=d+Math.round((s-d)*a),o=_+Math.round((c-_)*a),{css:h.toCss(r,n,o),rgba:h.toRgba(r,n,o)}},e.isOpaque=function(e){return 255==(255&e.rgba)},e.ensureContrastRatio=function(e,t,i){const s=_.ensureContrastRatio(e.rgba,t.rgba,i);if(s)return _.toColor(s>>24&255,s>>16&255,s>>8&255)},e.opaque=function(e){const t=(255|e.rgba)>>>0;return[r,n,o]=_.toChannels(t),{css:h.toCss(r,n,o),rgba:t}},e.opacity=t,e.multiplyOpacity=function(e,i){return a=255&e.rgba,t(e,a*i/255)},e.toColorRGB=function(e){return[e.rgba>>24&255,e.rgba>>16&255,e.rgba>>8&255]}}(c||(t.color=c={})),function(e){let t,i;if(!s.isNode){const e=document.createElement("canvas");e.width=1,e.height=1;const s=e.getContext("2d",{willReadFrequently:!0});s&&(t=s,t.globalCompositeOperation="copy",i=t.createLinearGradient(0,0,1,1))}e.toColor=function(e){if(e.match(/#[\da-f]{3,8}/i))switch(e.length){case 4:return r=parseInt(e.slice(1,2).repeat(2),16),n=parseInt(e.slice(2,3).repeat(2),16),o=parseInt(e.slice(3,4).repeat(2),16),_.toColor(r,n,o);case 5:return r=parseInt(e.slice(1,2).repeat(2),16),n=parseInt(e.slice(2,3).repeat(2),16),o=parseInt(e.slice(3,4).repeat(2),16),a=parseInt(e.slice(4,5).repeat(2),16),_.toColor(r,n,o,a);case 7:return{css:e,rgba:(parseInt(e.slice(1),16)<<8|255)>>>0};case 9:return{css:e,rgba:parseInt(e.slice(1),16)>>>0}}const s=e.match(/rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(,\s*(0|1|\d?\.(\d+))\s*)?\)/);if(s)return r=parseInt(s[1]),n=parseInt(s[2]),o=parseInt(s[3]),a=Math.round(255*(void 0===s[5]?1:parseFloat(s[5]))),_.toColor(r,n,o,a);if(!t||!i)throw new Error("css.toColor: Unsupported css format");if(t.fillStyle=i,t.fillStyle=e,"string"!=typeof t.fillStyle)throw new Error("css.toColor: Unsupported css format");if(t.fillRect(0,0,1,1),[r,n,o,a]=t.getImageData(0,0,1,1).data,255!==a)throw new Error("css.toColor: Unsupported css format");return{rgba:h.toRgba(r,n,o,a),css:e}}}(l||(t.css=l={})),function(e){function t(e,t,i){const s=e/255,r=t/255,n=i/255;return.2126*(s<=.03928?s/12.92:Math.pow((s+.055)/1.055,2.4))+.7152*(r<=.03928?r/12.92:Math.pow((r+.055)/1.055,2.4))+.0722*(n<=.03928?n/12.92:Math.pow((n+.055)/1.055,2.4))}e.relativeLuminance=function(e){return t(e>>16&255,e>>8&255,255&e)},e.relativeLuminance2=t}(d||(t.rgb=d={})),function(e){function t(e,t,i){const s=e>>24&255,r=e>>16&255,n=e>>8&255;let o=t>>24&255,a=t>>16&255,h=t>>8&255,c=f(d.relativeLuminance2(o,a,h),d.relativeLuminance2(s,r,n));for(;c0||a>0||h>0);)o-=Math.max(0,Math.ceil(.1*o)),a-=Math.max(0,Math.ceil(.1*a)),h-=Math.max(0,Math.ceil(.1*h)),c=f(d.relativeLuminance2(o,a,h),d.relativeLuminance2(s,r,n));return(o<<24|a<<16|h<<8|255)>>>0}function i(e,t,i){const s=e>>24&255,r=e>>16&255,n=e>>8&255;let o=t>>24&255,a=t>>16&255,h=t>>8&255,c=f(d.relativeLuminance2(o,a,h),d.relativeLuminance2(s,r,n));for(;c>>0}e.ensureContrastRatio=function(e,s,r){const n=d.relativeLuminance(e>>8),o=d.relativeLuminance(s>>8);if(f(n,o)>8));if(af(n,d.relativeLuminance(t>>8))?o:t}return o}const a=i(e,s,r),h=f(n,d.relativeLuminance(a>>8));if(hf(n,d.relativeLuminance(i>>8))?a:i}return a}},e.reduceLuminance=t,e.increaseLuminance=i,e.toChannels=function(e){return[e>>24&255,e>>16&255,e>>8&255,255&e]},e.toColor=function(e,t,i,s){return{css:h.toCss(e,t,i,s),rgba:h.toRgba(e,t,i,s)}}}(_||(t.rgba=_={})),t.toPaddedHex=u,t.contrastRatio=f},8969:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.CoreTerminal=void 0;const s=i(844),r=i(2585),n=i(4348),o=i(7866),a=i(744),h=i(7302),c=i(6975),l=i(8460),d=i(1753),_=i(1480),u=i(7994),f=i(9282),v=i(5435),p=i(5981),g=i(2660);let m=!1;class S extends s.Disposable{get onScroll(){return this._onScrollApi||(this._onScrollApi=this.register(new l.EventEmitter),this._onScroll.event((e=>{var t;null===(t=this._onScrollApi)||void 0===t||t.fire(e.position)}))),this._onScrollApi.event}get cols(){return this._bufferService.cols}get rows(){return this._bufferService.rows}get buffers(){return this._bufferService.buffers}get options(){return this.optionsService.options}set options(e){for(const t in e)this.optionsService.options[t]=e[t]}constructor(e){super(),this._windowsWrappingHeuristics=this.register(new s.MutableDisposable),this._onBinary=this.register(new l.EventEmitter),this.onBinary=this._onBinary.event,this._onData=this.register(new l.EventEmitter),this.onData=this._onData.event,this._onLineFeed=this.register(new l.EventEmitter),this.onLineFeed=this._onLineFeed.event,this._onResize=this.register(new l.EventEmitter),this.onResize=this._onResize.event,this._onWriteParsed=this.register(new l.EventEmitter),this.onWriteParsed=this._onWriteParsed.event,this._onScroll=this.register(new l.EventEmitter),this._instantiationService=new n.InstantiationService,this.optionsService=this.register(new h.OptionsService(e)),this._instantiationService.setService(r.IOptionsService,this.optionsService),this._bufferService=this.register(this._instantiationService.createInstance(a.BufferService)),this._instantiationService.setService(r.IBufferService,this._bufferService),this._logService=this.register(this._instantiationService.createInstance(o.LogService)),this._instantiationService.setService(r.ILogService,this._logService),this.coreService=this.register(this._instantiationService.createInstance(c.CoreService)),this._instantiationService.setService(r.ICoreService,this.coreService),this.coreMouseService=this.register(this._instantiationService.createInstance(d.CoreMouseService)),this._instantiationService.setService(r.ICoreMouseService,this.coreMouseService),this.unicodeService=this.register(this._instantiationService.createInstance(_.UnicodeService)),this._instantiationService.setService(r.IUnicodeService,this.unicodeService),this._charsetService=this._instantiationService.createInstance(u.CharsetService),this._instantiationService.setService(r.ICharsetService,this._charsetService),this._oscLinkService=this._instantiationService.createInstance(g.OscLinkService),this._instantiationService.setService(r.IOscLinkService,this._oscLinkService),this._inputHandler=this.register(new v.InputHandler(this._bufferService,this._charsetService,this.coreService,this._logService,this.optionsService,this._oscLinkService,this.coreMouseService,this.unicodeService)),this.register((0,l.forwardEvent)(this._inputHandler.onLineFeed,this._onLineFeed)),this.register(this._inputHandler),this.register((0,l.forwardEvent)(this._bufferService.onResize,this._onResize)),this.register((0,l.forwardEvent)(this.coreService.onData,this._onData)),this.register((0,l.forwardEvent)(this.coreService.onBinary,this._onBinary)),this.register(this.coreService.onRequestScrollToBottom((()=>this.scrollToBottom()))),this.register(this.coreService.onUserInput((()=>this._writeBuffer.handleUserInput()))),this.register(this.optionsService.onMultipleOptionChange(["windowsMode","windowsPty"],(()=>this._handleWindowsPtyOptionChange()))),this.register(this._bufferService.onScroll((e=>{this._onScroll.fire({position:this._bufferService.buffer.ydisp,source:0}),this._inputHandler.markRangeDirty(this._bufferService.buffer.scrollTop,this._bufferService.buffer.scrollBottom)}))),this.register(this._inputHandler.onScroll((e=>{this._onScroll.fire({position:this._bufferService.buffer.ydisp,source:0}),this._inputHandler.markRangeDirty(this._bufferService.buffer.scrollTop,this._bufferService.buffer.scrollBottom)}))),this._writeBuffer=this.register(new p.WriteBuffer(((e,t)=>this._inputHandler.parse(e,t)))),this.register((0,l.forwardEvent)(this._writeBuffer.onWriteParsed,this._onWriteParsed))}write(e,t){this._writeBuffer.write(e,t)}writeSync(e,t){this._logService.logLevel<=r.LogLevelEnum.WARN&&!m&&(this._logService.warn("writeSync is unreliable and will be removed soon."),m=!0),this._writeBuffer.writeSync(e,t)}resize(e,t){isNaN(e)||isNaN(t)||(e=Math.max(e,a.MINIMUM_COLS),t=Math.max(t,a.MINIMUM_ROWS),this._bufferService.resize(e,t))}scroll(e,t=!1){this._bufferService.scroll(e,t)}scrollLines(e,t,i){this._bufferService.scrollLines(e,t,i)}scrollPages(e){this.scrollLines(e*(this.rows-1))}scrollToTop(){this.scrollLines(-this._bufferService.buffer.ydisp)}scrollToBottom(){this.scrollLines(this._bufferService.buffer.ybase-this._bufferService.buffer.ydisp)}scrollToLine(e){const t=e-this._bufferService.buffer.ydisp;0!==t&&this.scrollLines(t)}registerEscHandler(e,t){return this._inputHandler.registerEscHandler(e,t)}registerDcsHandler(e,t){return this._inputHandler.registerDcsHandler(e,t)}registerCsiHandler(e,t){return this._inputHandler.registerCsiHandler(e,t)}registerOscHandler(e,t){return this._inputHandler.registerOscHandler(e,t)}_setup(){this._handleWindowsPtyOptionChange()}reset(){this._inputHandler.reset(),this._bufferService.reset(),this._charsetService.reset(),this.coreService.reset(),this.coreMouseService.reset()}_handleWindowsPtyOptionChange(){let e=!1;const t=this.optionsService.rawOptions.windowsPty;t&&void 0!==t.buildNumber&&void 0!==t.buildNumber?e=!!("conpty"===t.backend&&t.buildNumber<21376):this.optionsService.rawOptions.windowsMode&&(e=!0),e?this._enableWindowsWrappingHeuristics():this._windowsWrappingHeuristics.clear()}_enableWindowsWrappingHeuristics(){if(!this._windowsWrappingHeuristics.value){const e=[];e.push(this.onLineFeed(f.updateWindowsModeWrappedState.bind(null,this._bufferService))),e.push(this.registerCsiHandler({final:"H"},(()=>((0,f.updateWindowsModeWrappedState)(this._bufferService),!1)))),this._windowsWrappingHeuristics.value=(0,s.toDisposable)((()=>{for(const t of e)t.dispose()}))}}}t.CoreTerminal=S},8460:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.forwardEvent=t.EventEmitter=void 0,t.EventEmitter=class{constructor(){this._listeners=[],this._disposed=!1}get event(){return this._event||(this._event=e=>(this._listeners.push(e),{dispose:()=>{if(!this._disposed)for(let t=0;tt.fire(e)))}},5435:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.InputHandler=t.WindowsOptionsReportType=void 0;const n=i(2584),o=i(7116),a=i(2015),h=i(844),c=i(482),l=i(8437),d=i(8460),_=i(643),u=i(511),f=i(3734),v=i(2585),p=i(6242),g=i(6351),m=i(5941),S={"(":0,")":1,"*":2,"+":3,"-":1,".":2},C=131072;function b(e,t){if(e>24)return t.setWinLines||!1;switch(e){case 1:return!!t.restoreWin;case 2:return!!t.minimizeWin;case 3:return!!t.setWinPosition;case 4:return!!t.setWinSizePixels;case 5:return!!t.raiseWin;case 6:return!!t.lowerWin;case 7:return!!t.refreshWin;case 8:return!!t.setWinSizeChars;case 9:return!!t.maximizeWin;case 10:return!!t.fullscreenWin;case 11:return!!t.getWinState;case 13:return!!t.getWinPosition;case 14:return!!t.getWinSizePixels;case 15:return!!t.getScreenSizePixels;case 16:return!!t.getCellSizePixels;case 18:return!!t.getWinSizeChars;case 19:return!!t.getScreenSizeChars;case 20:return!!t.getIconTitle;case 21:return!!t.getWinTitle;case 22:return!!t.pushTitle;case 23:return!!t.popTitle;case 24:return!!t.setWinLines}return!1}var y;!function(e){e[e.GET_WIN_SIZE_PIXELS=0]="GET_WIN_SIZE_PIXELS",e[e.GET_CELL_SIZE_PIXELS=1]="GET_CELL_SIZE_PIXELS"}(y||(t.WindowsOptionsReportType=y={}));let w=0;class E extends h.Disposable{getAttrData(){return this._curAttrData}constructor(e,t,i,s,r,h,_,f,v=new a.EscapeSequenceParser){super(),this._bufferService=e,this._charsetService=t,this._coreService=i,this._logService=s,this._optionsService=r,this._oscLinkService=h,this._coreMouseService=_,this._unicodeService=f,this._parser=v,this._parseBuffer=new Uint32Array(4096),this._stringDecoder=new c.StringToUtf32,this._utf8Decoder=new c.Utf8ToUtf32,this._workCell=new u.CellData,this._windowTitle="",this._iconName="",this._windowTitleStack=[],this._iconNameStack=[],this._curAttrData=l.DEFAULT_ATTR_DATA.clone(),this._eraseAttrDataInternal=l.DEFAULT_ATTR_DATA.clone(),this._onRequestBell=this.register(new d.EventEmitter),this.onRequestBell=this._onRequestBell.event,this._onRequestRefreshRows=this.register(new d.EventEmitter),this.onRequestRefreshRows=this._onRequestRefreshRows.event,this._onRequestReset=this.register(new d.EventEmitter),this.onRequestReset=this._onRequestReset.event,this._onRequestSendFocus=this.register(new d.EventEmitter),this.onRequestSendFocus=this._onRequestSendFocus.event,this._onRequestSyncScrollBar=this.register(new d.EventEmitter),this.onRequestSyncScrollBar=this._onRequestSyncScrollBar.event,this._onRequestWindowsOptionsReport=this.register(new d.EventEmitter),this.onRequestWindowsOptionsReport=this._onRequestWindowsOptionsReport.event,this._onA11yChar=this.register(new d.EventEmitter),this.onA11yChar=this._onA11yChar.event,this._onA11yTab=this.register(new d.EventEmitter),this.onA11yTab=this._onA11yTab.event,this._onCursorMove=this.register(new d.EventEmitter),this.onCursorMove=this._onCursorMove.event,this._onLineFeed=this.register(new d.EventEmitter),this.onLineFeed=this._onLineFeed.event,this._onScroll=this.register(new d.EventEmitter),this.onScroll=this._onScroll.event,this._onTitleChange=this.register(new d.EventEmitter),this.onTitleChange=this._onTitleChange.event,this._onColor=this.register(new d.EventEmitter),this.onColor=this._onColor.event,this._parseStack={paused:!1,cursorStartX:0,cursorStartY:0,decodedLength:0,position:0},this._specialColors=[256,257,258],this.register(this._parser),this._dirtyRowTracker=new k(this._bufferService),this._activeBuffer=this._bufferService.buffer,this.register(this._bufferService.buffers.onBufferActivate((e=>this._activeBuffer=e.activeBuffer))),this._parser.setCsiHandlerFallback(((e,t)=>{this._logService.debug("Unknown CSI code: ",{identifier:this._parser.identToString(e),params:t.toArray()})})),this._parser.setEscHandlerFallback((e=>{this._logService.debug("Unknown ESC code: ",{identifier:this._parser.identToString(e)})})),this._parser.setExecuteHandlerFallback((e=>{this._logService.debug("Unknown EXECUTE code: ",{code:e})})),this._parser.setOscHandlerFallback(((e,t,i)=>{this._logService.debug("Unknown OSC code: ",{identifier:e,action:t,data:i})})),this._parser.setDcsHandlerFallback(((e,t,i)=>{"HOOK"===t&&(i=i.toArray()),this._logService.debug("Unknown DCS code: ",{identifier:this._parser.identToString(e),action:t,payload:i})})),this._parser.setPrintHandler(((e,t,i)=>this.print(e,t,i))),this._parser.registerCsiHandler({final:"@"},(e=>this.insertChars(e))),this._parser.registerCsiHandler({intermediates:" ",final:"@"},(e=>this.scrollLeft(e))),this._parser.registerCsiHandler({final:"A"},(e=>this.cursorUp(e))),this._parser.registerCsiHandler({intermediates:" ",final:"A"},(e=>this.scrollRight(e))),this._parser.registerCsiHandler({final:"B"},(e=>this.cursorDown(e))),this._parser.registerCsiHandler({final:"C"},(e=>this.cursorForward(e))),this._parser.registerCsiHandler({final:"D"},(e=>this.cursorBackward(e))),this._parser.registerCsiHandler({final:"E"},(e=>this.cursorNextLine(e))),this._parser.registerCsiHandler({final:"F"},(e=>this.cursorPrecedingLine(e))),this._parser.registerCsiHandler({final:"G"},(e=>this.cursorCharAbsolute(e))),this._parser.registerCsiHandler({final:"H"},(e=>this.cursorPosition(e))),this._parser.registerCsiHandler({final:"I"},(e=>this.cursorForwardTab(e))),this._parser.registerCsiHandler({final:"J"},(e=>this.eraseInDisplay(e,!1))),this._parser.registerCsiHandler({prefix:"?",final:"J"},(e=>this.eraseInDisplay(e,!0))),this._parser.registerCsiHandler({final:"K"},(e=>this.eraseInLine(e,!1))),this._parser.registerCsiHandler({prefix:"?",final:"K"},(e=>this.eraseInLine(e,!0))),this._parser.registerCsiHandler({final:"L"},(e=>this.insertLines(e))),this._parser.registerCsiHandler({final:"M"},(e=>this.deleteLines(e))),this._parser.registerCsiHandler({final:"P"},(e=>this.deleteChars(e))),this._parser.registerCsiHandler({final:"S"},(e=>this.scrollUp(e))),this._parser.registerCsiHandler({final:"T"},(e=>this.scrollDown(e))),this._parser.registerCsiHandler({final:"X"},(e=>this.eraseChars(e))),this._parser.registerCsiHandler({final:"Z"},(e=>this.cursorBackwardTab(e))),this._parser.registerCsiHandler({final:"`"},(e=>this.charPosAbsolute(e))),this._parser.registerCsiHandler({final:"a"},(e=>this.hPositionRelative(e))),this._parser.registerCsiHandler({final:"b"},(e=>this.repeatPrecedingCharacter(e))),this._parser.registerCsiHandler({final:"c"},(e=>this.sendDeviceAttributesPrimary(e))),this._parser.registerCsiHandler({prefix:">",final:"c"},(e=>this.sendDeviceAttributesSecondary(e))),this._parser.registerCsiHandler({final:"d"},(e=>this.linePosAbsolute(e))),this._parser.registerCsiHandler({final:"e"},(e=>this.vPositionRelative(e))),this._parser.registerCsiHandler({final:"f"},(e=>this.hVPosition(e))),this._parser.registerCsiHandler({final:"g"},(e=>this.tabClear(e))),this._parser.registerCsiHandler({final:"h"},(e=>this.setMode(e))),this._parser.registerCsiHandler({prefix:"?",final:"h"},(e=>this.setModePrivate(e))),this._parser.registerCsiHandler({final:"l"},(e=>this.resetMode(e))),this._parser.registerCsiHandler({prefix:"?",final:"l"},(e=>this.resetModePrivate(e))),this._parser.registerCsiHandler({final:"m"},(e=>this.charAttributes(e))),this._parser.registerCsiHandler({final:"n"},(e=>this.deviceStatus(e))),this._parser.registerCsiHandler({prefix:"?",final:"n"},(e=>this.deviceStatusPrivate(e))),this._parser.registerCsiHandler({intermediates:"!",final:"p"},(e=>this.softReset(e))),this._parser.registerCsiHandler({intermediates:" ",final:"q"},(e=>this.setCursorStyle(e))),this._parser.registerCsiHandler({final:"r"},(e=>this.setScrollRegion(e))),this._parser.registerCsiHandler({final:"s"},(e=>this.saveCursor(e))),this._parser.registerCsiHandler({final:"t"},(e=>this.windowOptions(e))),this._parser.registerCsiHandler({final:"u"},(e=>this.restoreCursor(e))),this._parser.registerCsiHandler({intermediates:"'",final:"}"},(e=>this.insertColumns(e))),this._parser.registerCsiHandler({intermediates:"'",final:"~"},(e=>this.deleteColumns(e))),this._parser.registerCsiHandler({intermediates:'"',final:"q"},(e=>this.selectProtected(e))),this._parser.registerCsiHandler({intermediates:"$",final:"p"},(e=>this.requestMode(e,!0))),this._parser.registerCsiHandler({prefix:"?",intermediates:"$",final:"p"},(e=>this.requestMode(e,!1))),this._parser.setExecuteHandler(n.C0.BEL,(()=>this.bell())),this._parser.setExecuteHandler(n.C0.LF,(()=>this.lineFeed())),this._parser.setExecuteHandler(n.C0.VT,(()=>this.lineFeed())),this._parser.setExecuteHandler(n.C0.FF,(()=>this.lineFeed())),this._parser.setExecuteHandler(n.C0.CR,(()=>this.carriageReturn())),this._parser.setExecuteHandler(n.C0.BS,(()=>this.backspace())),this._parser.setExecuteHandler(n.C0.HT,(()=>this.tab())),this._parser.setExecuteHandler(n.C0.SO,(()=>this.shiftOut())),this._parser.setExecuteHandler(n.C0.SI,(()=>this.shiftIn())),this._parser.setExecuteHandler(n.C1.IND,(()=>this.index())),this._parser.setExecuteHandler(n.C1.NEL,(()=>this.nextLine())),this._parser.setExecuteHandler(n.C1.HTS,(()=>this.tabSet())),this._parser.registerOscHandler(0,new p.OscHandler((e=>(this.setTitle(e),this.setIconName(e),!0)))),this._parser.registerOscHandler(1,new p.OscHandler((e=>this.setIconName(e)))),this._parser.registerOscHandler(2,new p.OscHandler((e=>this.setTitle(e)))),this._parser.registerOscHandler(4,new p.OscHandler((e=>this.setOrReportIndexedColor(e)))),this._parser.registerOscHandler(8,new p.OscHandler((e=>this.setHyperlink(e)))),this._parser.registerOscHandler(10,new p.OscHandler((e=>this.setOrReportFgColor(e)))),this._parser.registerOscHandler(11,new p.OscHandler((e=>this.setOrReportBgColor(e)))),this._parser.registerOscHandler(12,new p.OscHandler((e=>this.setOrReportCursorColor(e)))),this._parser.registerOscHandler(104,new p.OscHandler((e=>this.restoreIndexedColor(e)))),this._parser.registerOscHandler(110,new p.OscHandler((e=>this.restoreFgColor(e)))),this._parser.registerOscHandler(111,new p.OscHandler((e=>this.restoreBgColor(e)))),this._parser.registerOscHandler(112,new p.OscHandler((e=>this.restoreCursorColor(e)))),this._parser.registerEscHandler({final:"7"},(()=>this.saveCursor())),this._parser.registerEscHandler({final:"8"},(()=>this.restoreCursor())),this._parser.registerEscHandler({final:"D"},(()=>this.index())),this._parser.registerEscHandler({final:"E"},(()=>this.nextLine())),this._parser.registerEscHandler({final:"H"},(()=>this.tabSet())),this._parser.registerEscHandler({final:"M"},(()=>this.reverseIndex())),this._parser.registerEscHandler({final:"="},(()=>this.keypadApplicationMode())),this._parser.registerEscHandler({final:">"},(()=>this.keypadNumericMode())),this._parser.registerEscHandler({final:"c"},(()=>this.fullReset())),this._parser.registerEscHandler({final:"n"},(()=>this.setgLevel(2))),this._parser.registerEscHandler({final:"o"},(()=>this.setgLevel(3))),this._parser.registerEscHandler({final:"|"},(()=>this.setgLevel(3))),this._parser.registerEscHandler({final:"}"},(()=>this.setgLevel(2))),this._parser.registerEscHandler({final:"~"},(()=>this.setgLevel(1))),this._parser.registerEscHandler({intermediates:"%",final:"@"},(()=>this.selectDefaultCharset())),this._parser.registerEscHandler({intermediates:"%",final:"G"},(()=>this.selectDefaultCharset()));for(const e in o.CHARSETS)this._parser.registerEscHandler({intermediates:"(",final:e},(()=>this.selectCharset("("+e))),this._parser.registerEscHandler({intermediates:")",final:e},(()=>this.selectCharset(")"+e))),this._parser.registerEscHandler({intermediates:"*",final:e},(()=>this.selectCharset("*"+e))),this._parser.registerEscHandler({intermediates:"+",final:e},(()=>this.selectCharset("+"+e))),this._parser.registerEscHandler({intermediates:"-",final:e},(()=>this.selectCharset("-"+e))),this._parser.registerEscHandler({intermediates:".",final:e},(()=>this.selectCharset("."+e))),this._parser.registerEscHandler({intermediates:"/",final:e},(()=>this.selectCharset("/"+e)));this._parser.registerEscHandler({intermediates:"#",final:"8"},(()=>this.screenAlignmentPattern())),this._parser.setErrorHandler((e=>(this._logService.error("Parsing error: ",e),e))),this._parser.registerDcsHandler({intermediates:"$",final:"q"},new g.DcsHandler(((e,t)=>this.requestStatusString(e,t))))}_preserveStack(e,t,i,s){this._parseStack.paused=!0,this._parseStack.cursorStartX=e,this._parseStack.cursorStartY=t,this._parseStack.decodedLength=i,this._parseStack.position=s}_logSlowResolvingAsync(e){this._logService.logLevel<=v.LogLevelEnum.WARN&&Promise.race([e,new Promise(((e,t)=>setTimeout((()=>t("#SLOW_TIMEOUT")),5e3)))]).catch((e=>{if("#SLOW_TIMEOUT"!==e)throw e;console.warn("async parser handler taking longer than 5000 ms")}))}_getCurrentLinkId(){return this._curAttrData.extended.urlId}parse(e,t){let i,s=this._activeBuffer.x,r=this._activeBuffer.y,n=0;const o=this._parseStack.paused;if(o){if(i=this._parser.parse(this._parseBuffer,this._parseStack.decodedLength,t))return this._logSlowResolvingAsync(i),i;s=this._parseStack.cursorStartX,r=this._parseStack.cursorStartY,this._parseStack.paused=!1,e.length>C&&(n=this._parseStack.position+C)}if(this._logService.logLevel<=v.LogLevelEnum.DEBUG&&this._logService.debug("parsing data"+("string"==typeof e?` "${e}"`:` "${Array.prototype.map.call(e,(e=>String.fromCharCode(e))).join("")}"`),"string"==typeof e?e.split("").map((e=>e.charCodeAt(0))):e),this._parseBuffer.lengthC)for(let t=n;t0&&2===u.getWidth(this._activeBuffer.x-1)&&u.setCellFromCodePoint(this._activeBuffer.x-1,0,1,d.fg,d.bg,d.extended);for(let f=t;f=a)if(h){for(;this._activeBuffer.x=this._bufferService.rows&&(this._activeBuffer.y=this._bufferService.rows-1),this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y).isWrapped=!0),u=this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y)}else if(this._activeBuffer.x=a-1,2===r)continue;if(l&&(u.insertCells(this._activeBuffer.x,r,this._activeBuffer.getNullCell(d),d),2===u.getWidth(a-1)&&u.setCellFromCodePoint(a-1,_.NULL_CELL_CODE,_.NULL_CELL_WIDTH,d.fg,d.bg,d.extended)),u.setCellFromCodePoint(this._activeBuffer.x++,s,r,d.fg,d.bg,d.extended),r>0)for(;--r;)u.setCellFromCodePoint(this._activeBuffer.x++,0,0,d.fg,d.bg,d.extended)}else u.getWidth(this._activeBuffer.x-1)?u.addCodepointToCell(this._activeBuffer.x-1,s):u.addCodepointToCell(this._activeBuffer.x-2,s)}i-t>0&&(u.loadCell(this._activeBuffer.x-1,this._workCell),2===this._workCell.getWidth()||this._workCell.getCode()>65535?this._parser.precedingCodepoint=0:this._workCell.isCombined()?this._parser.precedingCodepoint=this._workCell.getChars().charCodeAt(0):this._parser.precedingCodepoint=this._workCell.content),this._activeBuffer.x0&&0===u.getWidth(this._activeBuffer.x)&&!u.hasContent(this._activeBuffer.x)&&u.setCellFromCodePoint(this._activeBuffer.x,0,1,d.fg,d.bg,d.extended),this._dirtyRowTracker.markDirty(this._activeBuffer.y)}registerCsiHandler(e,t){return"t"!==e.final||e.prefix||e.intermediates?this._parser.registerCsiHandler(e,t):this._parser.registerCsiHandler(e,(e=>!b(e.params[0],this._optionsService.rawOptions.windowOptions)||t(e)))}registerDcsHandler(e,t){return this._parser.registerDcsHandler(e,new g.DcsHandler(t))}registerEscHandler(e,t){return this._parser.registerEscHandler(e,t)}registerOscHandler(e,t){return this._parser.registerOscHandler(e,new p.OscHandler(t))}bell(){return this._onRequestBell.fire(),!0}lineFeed(){return this._dirtyRowTracker.markDirty(this._activeBuffer.y),this._optionsService.rawOptions.convertEol&&(this._activeBuffer.x=0),this._activeBuffer.y++,this._activeBuffer.y===this._activeBuffer.scrollBottom+1?(this._activeBuffer.y--,this._bufferService.scroll(this._eraseAttrData())):this._activeBuffer.y>=this._bufferService.rows?this._activeBuffer.y=this._bufferService.rows-1:this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y).isWrapped=!1,this._activeBuffer.x>=this._bufferService.cols&&this._activeBuffer.x--,this._dirtyRowTracker.markDirty(this._activeBuffer.y),this._onLineFeed.fire(),!0}carriageReturn(){return this._activeBuffer.x=0,!0}backspace(){var e;if(!this._coreService.decPrivateModes.reverseWraparound)return this._restrictCursor(),this._activeBuffer.x>0&&this._activeBuffer.x--,!0;if(this._restrictCursor(this._bufferService.cols),this._activeBuffer.x>0)this._activeBuffer.x--;else if(0===this._activeBuffer.x&&this._activeBuffer.y>this._activeBuffer.scrollTop&&this._activeBuffer.y<=this._activeBuffer.scrollBottom&&(null===(e=this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y))||void 0===e?void 0:e.isWrapped)){this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y).isWrapped=!1,this._activeBuffer.y--,this._activeBuffer.x=this._bufferService.cols-1;const e=this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y);e.hasWidth(this._activeBuffer.x)&&!e.hasContent(this._activeBuffer.x)&&this._activeBuffer.x--}return this._restrictCursor(),!0}tab(){if(this._activeBuffer.x>=this._bufferService.cols)return!0;const e=this._activeBuffer.x;return this._activeBuffer.x=this._activeBuffer.nextStop(),this._optionsService.rawOptions.screenReaderMode&&this._onA11yTab.fire(this._activeBuffer.x-e),!0}shiftOut(){return this._charsetService.setgLevel(1),!0}shiftIn(){return this._charsetService.setgLevel(0),!0}_restrictCursor(e=this._bufferService.cols-1){this._activeBuffer.x=Math.min(e,Math.max(0,this._activeBuffer.x)),this._activeBuffer.y=this._coreService.decPrivateModes.origin?Math.min(this._activeBuffer.scrollBottom,Math.max(this._activeBuffer.scrollTop,this._activeBuffer.y)):Math.min(this._bufferService.rows-1,Math.max(0,this._activeBuffer.y)),this._dirtyRowTracker.markDirty(this._activeBuffer.y)}_setCursor(e,t){this._dirtyRowTracker.markDirty(this._activeBuffer.y),this._coreService.decPrivateModes.origin?(this._activeBuffer.x=e,this._activeBuffer.y=this._activeBuffer.scrollTop+t):(this._activeBuffer.x=e,this._activeBuffer.y=t),this._restrictCursor(),this._dirtyRowTracker.markDirty(this._activeBuffer.y)}_moveCursor(e,t){this._restrictCursor(),this._setCursor(this._activeBuffer.x+e,this._activeBuffer.y+t)}cursorUp(e){const t=this._activeBuffer.y-this._activeBuffer.scrollTop;return t>=0?this._moveCursor(0,-Math.min(t,e.params[0]||1)):this._moveCursor(0,-(e.params[0]||1)),!0}cursorDown(e){const t=this._activeBuffer.scrollBottom-this._activeBuffer.y;return t>=0?this._moveCursor(0,Math.min(t,e.params[0]||1)):this._moveCursor(0,e.params[0]||1),!0}cursorForward(e){return this._moveCursor(e.params[0]||1,0),!0}cursorBackward(e){return this._moveCursor(-(e.params[0]||1),0),!0}cursorNextLine(e){return this.cursorDown(e),this._activeBuffer.x=0,!0}cursorPrecedingLine(e){return this.cursorUp(e),this._activeBuffer.x=0,!0}cursorCharAbsolute(e){return this._setCursor((e.params[0]||1)-1,this._activeBuffer.y),!0}cursorPosition(e){return this._setCursor(e.length>=2?(e.params[1]||1)-1:0,(e.params[0]||1)-1),!0}charPosAbsolute(e){return this._setCursor((e.params[0]||1)-1,this._activeBuffer.y),!0}hPositionRelative(e){return this._moveCursor(e.params[0]||1,0),!0}linePosAbsolute(e){return this._setCursor(this._activeBuffer.x,(e.params[0]||1)-1),!0}vPositionRelative(e){return this._moveCursor(0,e.params[0]||1),!0}hVPosition(e){return this.cursorPosition(e),!0}tabClear(e){const t=e.params[0];return 0===t?delete this._activeBuffer.tabs[this._activeBuffer.x]:3===t&&(this._activeBuffer.tabs={}),!0}cursorForwardTab(e){if(this._activeBuffer.x>=this._bufferService.cols)return!0;let t=e.params[0]||1;for(;t--;)this._activeBuffer.x=this._activeBuffer.nextStop();return!0}cursorBackwardTab(e){if(this._activeBuffer.x>=this._bufferService.cols)return!0;let t=e.params[0]||1;for(;t--;)this._activeBuffer.x=this._activeBuffer.prevStop();return!0}selectProtected(e){const t=e.params[0];return 1===t&&(this._curAttrData.bg|=536870912),2!==t&&0!==t||(this._curAttrData.bg&=-536870913),!0}_eraseInBufferLine(e,t,i,s=!1,r=!1){const n=this._activeBuffer.lines.get(this._activeBuffer.ybase+e);n.replaceCells(t,i,this._activeBuffer.getNullCell(this._eraseAttrData()),this._eraseAttrData(),r),s&&(n.isWrapped=!1)}_resetBufferLine(e,t=!1){const i=this._activeBuffer.lines.get(this._activeBuffer.ybase+e);i&&(i.fill(this._activeBuffer.getNullCell(this._eraseAttrData()),t),this._bufferService.buffer.clearMarkers(this._activeBuffer.ybase+e),i.isWrapped=!1)}eraseInDisplay(e,t=!1){let i;switch(this._restrictCursor(this._bufferService.cols),e.params[0]){case 0:for(i=this._activeBuffer.y,this._dirtyRowTracker.markDirty(i),this._eraseInBufferLine(i++,this._activeBuffer.x,this._bufferService.cols,0===this._activeBuffer.x,t);i=this._bufferService.cols&&(this._activeBuffer.lines.get(i+1).isWrapped=!1);i--;)this._resetBufferLine(i,t);this._dirtyRowTracker.markDirty(0);break;case 2:for(i=this._bufferService.rows,this._dirtyRowTracker.markDirty(i-1);i--;)this._resetBufferLine(i,t);this._dirtyRowTracker.markDirty(0);break;case 3:const e=this._activeBuffer.lines.length-this._bufferService.rows;e>0&&(this._activeBuffer.lines.trimStart(e),this._activeBuffer.ybase=Math.max(this._activeBuffer.ybase-e,0),this._activeBuffer.ydisp=Math.max(this._activeBuffer.ydisp-e,0),this._onScroll.fire(0))}return!0}eraseInLine(e,t=!1){switch(this._restrictCursor(this._bufferService.cols),e.params[0]){case 0:this._eraseInBufferLine(this._activeBuffer.y,this._activeBuffer.x,this._bufferService.cols,0===this._activeBuffer.x,t);break;case 1:this._eraseInBufferLine(this._activeBuffer.y,0,this._activeBuffer.x+1,!1,t);break;case 2:this._eraseInBufferLine(this._activeBuffer.y,0,this._bufferService.cols,!0,t)}return this._dirtyRowTracker.markDirty(this._activeBuffer.y),!0}insertLines(e){this._restrictCursor();let t=e.params[0]||1;if(this._activeBuffer.y>this._activeBuffer.scrollBottom||this._activeBuffer.ythis._activeBuffer.scrollBottom||this._activeBuffer.ythis._activeBuffer.scrollBottom||this._activeBuffer.ythis._activeBuffer.scrollBottom||this._activeBuffer.ythis._activeBuffer.scrollBottom||this._activeBuffer.ythis._activeBuffer.scrollBottom||this._activeBuffer.y0||(this._is("xterm")||this._is("rxvt-unicode")||this._is("screen")?this._coreService.triggerDataEvent(n.C0.ESC+"[?1;2c"):this._is("linux")&&this._coreService.triggerDataEvent(n.C0.ESC+"[?6c")),!0}sendDeviceAttributesSecondary(e){return e.params[0]>0||(this._is("xterm")?this._coreService.triggerDataEvent(n.C0.ESC+"[>0;276;0c"):this._is("rxvt-unicode")?this._coreService.triggerDataEvent(n.C0.ESC+"[>85;95;0c"):this._is("linux")?this._coreService.triggerDataEvent(e.params[0]+"c"):this._is("screen")&&this._coreService.triggerDataEvent(n.C0.ESC+"[>83;40003;0c")),!0}_is(e){return 0===(this._optionsService.rawOptions.termName+"").indexOf(e)}setMode(e){for(let t=0;te?1:2,u=e.params[0];return f=u,v=t?2===u?4:4===u?_(o.modes.insertMode):12===u?3:20===u?_(d.convertEol):0:1===u?_(i.applicationCursorKeys):3===u?d.windowOptions.setWinLines?80===h?2:132===h?1:0:0:6===u?_(i.origin):7===u?_(i.wraparound):8===u?3:9===u?_("X10"===s):12===u?_(d.cursorBlink):25===u?_(!o.isCursorHidden):45===u?_(i.reverseWraparound):66===u?_(i.applicationKeypad):67===u?4:1e3===u?_("VT200"===s):1002===u?_("DRAG"===s):1003===u?_("ANY"===s):1004===u?_(i.sendFocus):1005===u?4:1006===u?_("SGR"===r):1015===u?4:1016===u?_("SGR_PIXELS"===r):1048===u?1:47===u||1047===u||1049===u?_(c===l):2004===u?_(i.bracketedPasteMode):0,o.triggerDataEvent(`${n.C0.ESC}[${t?"":"?"}${f};${v}$y`),!0;var f,v}_updateAttrColor(e,t,i,s,r){return 2===t?(e|=50331648,e&=-16777216,e|=f.AttributeData.fromColorRGB([i,s,r])):5===t&&(e&=-50331904,e|=33554432|255&i),e}_extractColor(e,t,i){const s=[0,0,-1,0,0,0];let r=0,n=0;do{if(s[n+r]=e.params[t+n],e.hasSubParams(t+n)){const i=e.getSubParams(t+n);let o=0;do{5===s[1]&&(r=1),s[n+o+1+r]=i[o]}while(++o=2||2===s[1]&&n+r>=5)break;s[1]&&(r=1)}while(++n+t5)&&(e=1),t.extended.underlineStyle=e,t.fg|=268435456,0===e&&(t.fg&=-268435457),t.updateExtended()}_processSGR0(e){e.fg=l.DEFAULT_ATTR_DATA.fg,e.bg=l.DEFAULT_ATTR_DATA.bg,e.extended=e.extended.clone(),e.extended.underlineStyle=0,e.extended.underlineColor&=-67108864,e.updateExtended()}charAttributes(e){if(1===e.length&&0===e.params[0])return this._processSGR0(this._curAttrData),!0;const t=e.length;let i;const s=this._curAttrData;for(let r=0;r=30&&i<=37?(s.fg&=-50331904,s.fg|=16777216|i-30):i>=40&&i<=47?(s.bg&=-50331904,s.bg|=16777216|i-40):i>=90&&i<=97?(s.fg&=-50331904,s.fg|=16777224|i-90):i>=100&&i<=107?(s.bg&=-50331904,s.bg|=16777224|i-100):0===i?this._processSGR0(s):1===i?s.fg|=134217728:3===i?s.bg|=67108864:4===i?(s.fg|=268435456,this._processUnderline(e.hasSubParams(r)?e.getSubParams(r)[0]:1,s)):5===i?s.fg|=536870912:7===i?s.fg|=67108864:8===i?s.fg|=1073741824:9===i?s.fg|=2147483648:2===i?s.bg|=134217728:21===i?this._processUnderline(2,s):22===i?(s.fg&=-134217729,s.bg&=-134217729):23===i?s.bg&=-67108865:24===i?(s.fg&=-268435457,this._processUnderline(0,s)):25===i?s.fg&=-536870913:27===i?s.fg&=-67108865:28===i?s.fg&=-1073741825:29===i?s.fg&=2147483647:39===i?(s.fg&=-67108864,s.fg|=16777215&l.DEFAULT_ATTR_DATA.fg):49===i?(s.bg&=-67108864,s.bg|=16777215&l.DEFAULT_ATTR_DATA.bg):38===i||48===i||58===i?r+=this._extractColor(e,r,s):53===i?s.bg|=1073741824:55===i?s.bg&=-1073741825:59===i?(s.extended=s.extended.clone(),s.extended.underlineColor=-1,s.updateExtended()):100===i?(s.fg&=-67108864,s.fg|=16777215&l.DEFAULT_ATTR_DATA.fg,s.bg&=-67108864,s.bg|=16777215&l.DEFAULT_ATTR_DATA.bg):this._logService.debug("Unknown SGR attribute: %d.",i);return!0}deviceStatus(e){switch(e.params[0]){case 5:this._coreService.triggerDataEvent(`${n.C0.ESC}[0n`);break;case 6:const e=this._activeBuffer.y+1,t=this._activeBuffer.x+1;this._coreService.triggerDataEvent(`${n.C0.ESC}[${e};${t}R`)}return!0}deviceStatusPrivate(e){if(6===e.params[0]){const e=this._activeBuffer.y+1,t=this._activeBuffer.x+1;this._coreService.triggerDataEvent(`${n.C0.ESC}[?${e};${t}R`)}return!0}softReset(e){return this._coreService.isCursorHidden=!1,this._onRequestSyncScrollBar.fire(),this._activeBuffer.scrollTop=0,this._activeBuffer.scrollBottom=this._bufferService.rows-1,this._curAttrData=l.DEFAULT_ATTR_DATA.clone(),this._coreService.reset(),this._charsetService.reset(),this._activeBuffer.savedX=0,this._activeBuffer.savedY=this._activeBuffer.ybase,this._activeBuffer.savedCurAttrData.fg=this._curAttrData.fg,this._activeBuffer.savedCurAttrData.bg=this._curAttrData.bg,this._activeBuffer.savedCharset=this._charsetService.charset,this._coreService.decPrivateModes.origin=!1,!0}setCursorStyle(e){const t=e.params[0]||1;switch(t){case 1:case 2:this._optionsService.options.cursorStyle="block";break;case 3:case 4:this._optionsService.options.cursorStyle="underline";break;case 5:case 6:this._optionsService.options.cursorStyle="bar"}const i=t%2==1;return this._optionsService.options.cursorBlink=i,!0}setScrollRegion(e){const t=e.params[0]||1;let i;return(e.length<2||(i=e.params[1])>this._bufferService.rows||0===i)&&(i=this._bufferService.rows),i>t&&(this._activeBuffer.scrollTop=t-1,this._activeBuffer.scrollBottom=i-1,this._setCursor(0,0)),!0}windowOptions(e){if(!b(e.params[0],this._optionsService.rawOptions.windowOptions))return!0;const t=e.length>1?e.params[1]:0;switch(e.params[0]){case 14:2!==t&&this._onRequestWindowsOptionsReport.fire(y.GET_WIN_SIZE_PIXELS);break;case 16:this._onRequestWindowsOptionsReport.fire(y.GET_CELL_SIZE_PIXELS);break;case 18:this._bufferService&&this._coreService.triggerDataEvent(`${n.C0.ESC}[8;${this._bufferService.rows};${this._bufferService.cols}t`);break;case 22:0!==t&&2!==t||(this._windowTitleStack.push(this._windowTitle),this._windowTitleStack.length>10&&this._windowTitleStack.shift()),0!==t&&1!==t||(this._iconNameStack.push(this._iconName),this._iconNameStack.length>10&&this._iconNameStack.shift());break;case 23:0!==t&&2!==t||this._windowTitleStack.length&&this.setTitle(this._windowTitleStack.pop()),0!==t&&1!==t||this._iconNameStack.length&&this.setIconName(this._iconNameStack.pop())}return!0}saveCursor(e){return this._activeBuffer.savedX=this._activeBuffer.x,this._activeBuffer.savedY=this._activeBuffer.ybase+this._activeBuffer.y,this._activeBuffer.savedCurAttrData.fg=this._curAttrData.fg,this._activeBuffer.savedCurAttrData.bg=this._curAttrData.bg,this._activeBuffer.savedCharset=this._charsetService.charset,!0}restoreCursor(e){return this._activeBuffer.x=this._activeBuffer.savedX||0,this._activeBuffer.y=Math.max(this._activeBuffer.savedY-this._activeBuffer.ybase,0),this._curAttrData.fg=this._activeBuffer.savedCurAttrData.fg,this._curAttrData.bg=this._activeBuffer.savedCurAttrData.bg,this._charsetService.charset=this._savedCharset,this._activeBuffer.savedCharset&&(this._charsetService.charset=this._activeBuffer.savedCharset),this._restrictCursor(),!0}setTitle(e){return this._windowTitle=e,this._onTitleChange.fire(e),!0}setIconName(e){return this._iconName=e,!0}setOrReportIndexedColor(e){const t=[],i=e.split(";");for(;i.length>1;){const e=i.shift(),s=i.shift();if(/^\d+$/.exec(e)){const i=parseInt(e);if(L(i))if("?"===s)t.push({type:0,index:i});else{const e=(0,m.parseColor)(s);e&&t.push({type:1,index:i,color:e})}}}return t.length&&this._onColor.fire(t),!0}setHyperlink(e){const t=e.split(";");return!(t.length<2)&&(t[1]?this._createHyperlink(t[0],t[1]):!t[0]&&this._finishHyperlink())}_createHyperlink(e,t){this._getCurrentLinkId()&&this._finishHyperlink();const i=e.split(":");let s;const r=i.findIndex((e=>e.startsWith("id=")));return-1!==r&&(s=i[r].slice(3)||void 0),this._curAttrData.extended=this._curAttrData.extended.clone(),this._curAttrData.extended.urlId=this._oscLinkService.registerLink({id:s,uri:t}),this._curAttrData.updateExtended(),!0}_finishHyperlink(){return this._curAttrData.extended=this._curAttrData.extended.clone(),this._curAttrData.extended.urlId=0,this._curAttrData.updateExtended(),!0}_setOrReportSpecialColor(e,t){const i=e.split(";");for(let e=0;e=this._specialColors.length);++e,++t)if("?"===i[e])this._onColor.fire([{type:0,index:this._specialColors[t]}]);else{const s=(0,m.parseColor)(i[e]);s&&this._onColor.fire([{type:1,index:this._specialColors[t],color:s}])}return!0}setOrReportFgColor(e){return this._setOrReportSpecialColor(e,0)}setOrReportBgColor(e){return this._setOrReportSpecialColor(e,1)}setOrReportCursorColor(e){return this._setOrReportSpecialColor(e,2)}restoreIndexedColor(e){if(!e)return this._onColor.fire([{type:2}]),!0;const t=[],i=e.split(";");for(let e=0;e=this._bufferService.rows&&(this._activeBuffer.y=this._bufferService.rows-1),this._restrictCursor(),!0}tabSet(){return this._activeBuffer.tabs[this._activeBuffer.x]=!0,!0}reverseIndex(){if(this._restrictCursor(),this._activeBuffer.y===this._activeBuffer.scrollTop){const e=this._activeBuffer.scrollBottom-this._activeBuffer.scrollTop;this._activeBuffer.lines.shiftElements(this._activeBuffer.ybase+this._activeBuffer.y,e,1),this._activeBuffer.lines.set(this._activeBuffer.ybase+this._activeBuffer.y,this._activeBuffer.getBlankLine(this._eraseAttrData())),this._dirtyRowTracker.markRangeDirty(this._activeBuffer.scrollTop,this._activeBuffer.scrollBottom)}else this._activeBuffer.y--,this._restrictCursor();return!0}fullReset(){return this._parser.reset(),this._onRequestReset.fire(),!0}reset(){this._curAttrData=l.DEFAULT_ATTR_DATA.clone(),this._eraseAttrDataInternal=l.DEFAULT_ATTR_DATA.clone()}_eraseAttrData(){return this._eraseAttrDataInternal.bg&=-67108864,this._eraseAttrDataInternal.bg|=67108863&this._curAttrData.bg,this._eraseAttrDataInternal}setgLevel(e){return this._charsetService.setgLevel(e),!0}screenAlignmentPattern(){const e=new u.CellData;e.content=1<<22|"E".charCodeAt(0),e.fg=this._curAttrData.fg,e.bg=this._curAttrData.bg,this._setCursor(0,0);for(let t=0;t(this._coreService.triggerDataEvent(`${n.C0.ESC}${e}${n.C0.ESC}\\`),!0))('"q'===e?`P1$r${this._curAttrData.isProtected()?1:0}"q`:'"p'===e?'P1$r61;1"p':"r"===e?`P1$r${i.scrollTop+1};${i.scrollBottom+1}r`:"m"===e?"P1$r0m":" q"===e?`P1$r${{block:2,underline:4,bar:6}[s.cursorStyle]-(s.cursorBlink?1:0)} q`:"P0$r")}markRangeDirty(e,t){this._dirtyRowTracker.markRangeDirty(e,t)}}t.InputHandler=E;let k=class{constructor(e){this._bufferService=e,this.clearRange()}clearRange(){this.start=this._bufferService.buffer.y,this.end=this._bufferService.buffer.y}markDirty(e){ethis.end&&(this.end=e)}markRangeDirty(e,t){e>t&&(w=e,e=t,t=w),ethis.end&&(this.end=t)}markAllDirty(){this.markRangeDirty(0,this._bufferService.rows-1)}};function L(e){return 0<=e&&e<256}k=s([r(0,v.IBufferService)],k)},844:(e,t)=>{function i(e){for(const t of e)t.dispose();e.length=0}Object.defineProperty(t,"__esModule",{value:!0}),t.getDisposeArrayDisposable=t.disposeArray=t.toDisposable=t.MutableDisposable=t.Disposable=void 0,t.Disposable=class{constructor(){this._disposables=[],this._isDisposed=!1}dispose(){this._isDisposed=!0;for(const e of this._disposables)e.dispose();this._disposables.length=0}register(e){return this._disposables.push(e),e}unregister(e){const t=this._disposables.indexOf(e);-1!==t&&this._disposables.splice(t,1)}},t.MutableDisposable=class{constructor(){this._isDisposed=!1}get value(){return this._isDisposed?void 0:this._value}set value(e){var t;this._isDisposed||e===this._value||(null===(t=this._value)||void 0===t||t.dispose(),this._value=e)}clear(){this.value=void 0}dispose(){var e;this._isDisposed=!0,null===(e=this._value)||void 0===e||e.dispose(),this._value=void 0}},t.toDisposable=function(e){return{dispose:e}},t.disposeArray=i,t.getDisposeArrayDisposable=function(e){return{dispose:()=>i(e)}}},1505:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.FourKeyMap=t.TwoKeyMap=void 0;class i{constructor(){this._data={}}set(e,t,i){this._data[e]||(this._data[e]={}),this._data[e][t]=i}get(e,t){return this._data[e]?this._data[e][t]:void 0}clear(){this._data={}}}t.TwoKeyMap=i,t.FourKeyMap=class{constructor(){this._data=new i}set(e,t,s,r,n){this._data.get(e,t)||this._data.set(e,t,new i),this._data.get(e,t).set(s,r,n)}get(e,t,i,s){var r;return null===(r=this._data.get(e,t))||void 0===r?void 0:r.get(i,s)}clear(){this._data.clear()}}},6114:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.isChromeOS=t.isLinux=t.isWindows=t.isIphone=t.isIpad=t.isMac=t.getSafariVersion=t.isSafari=t.isLegacyEdge=t.isFirefox=t.isNode=void 0,t.isNode="undefined"==typeof navigator;const i=t.isNode?"node":navigator.userAgent,s=t.isNode?"node":navigator.platform;t.isFirefox=i.includes("Firefox"),t.isLegacyEdge=i.includes("Edge"),t.isSafari=/^((?!chrome|android).)*safari/i.test(i),t.getSafariVersion=function(){if(!t.isSafari)return 0;const e=i.match(/Version\/(\d+)/);return null===e||e.length<2?0:parseInt(e[1])},t.isMac=["Macintosh","MacIntel","MacPPC","Mac68K"].includes(s),t.isIpad="iPad"===s,t.isIphone="iPhone"===s,t.isWindows=["Windows","Win16","Win32","WinCE"].includes(s),t.isLinux=s.indexOf("Linux")>=0,t.isChromeOS=/\bCrOS\b/.test(i)},6106:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.SortedList=void 0;let i=0;t.SortedList=class{constructor(e){this._getKey=e,this._array=[]}clear(){this._array.length=0}insert(e){0!==this._array.length?(i=this._search(this._getKey(e)),this._array.splice(i,0,e)):this._array.push(e)}delete(e){if(0===this._array.length)return!1;const t=this._getKey(e);if(void 0===t)return!1;if(i=this._search(t),-1===i)return!1;if(this._getKey(this._array[i])!==t)return!1;do{if(this._array[i]===e)return this._array.splice(i,1),!0}while(++i=this._array.length)&&this._getKey(this._array[i])===e))do{yield this._array[i]}while(++i=this._array.length)&&this._getKey(this._array[i])===e))do{t(this._array[i])}while(++i=t;){let s=t+i>>1;const r=this._getKey(this._array[s]);if(r>e)i=s-1;else{if(!(r0&&this._getKey(this._array[s-1])===e;)s--;return s}t=s+1}}return t}}},7226:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.DebouncedIdleTask=t.IdleTaskQueue=t.PriorityTaskQueue=void 0;const s=i(6114);class r{constructor(){this._tasks=[],this._i=0}enqueue(e){this._tasks.push(e),this._start()}flush(){for(;this._ir)return s-t<-20&&console.warn(`task queue exceeded allotted deadline by ${Math.abs(Math.round(s-t))}ms`),void this._start();s=r}this.clear()}}class n extends r{_requestCallback(e){return setTimeout((()=>e(this._createDeadline(16))))}_cancelCallback(e){clearTimeout(e)}_createDeadline(e){const t=Date.now()+e;return{timeRemaining:()=>Math.max(0,t-Date.now())}}}t.PriorityTaskQueue=n,t.IdleTaskQueue=!s.isNode&&"requestIdleCallback"in window?class extends r{_requestCallback(e){return requestIdleCallback(e)}_cancelCallback(e){cancelIdleCallback(e)}}:n,t.DebouncedIdleTask=class{constructor(){this._queue=new t.IdleTaskQueue}set(e){this._queue.clear(),this._queue.enqueue(e)}flush(){this._queue.flush()}}},9282:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.updateWindowsModeWrappedState=void 0;const s=i(643);t.updateWindowsModeWrappedState=function(e){const t=e.buffer.lines.get(e.buffer.ybase+e.buffer.y-1),i=null==t?void 0:t.get(e.cols-1),r=e.buffer.lines.get(e.buffer.ybase+e.buffer.y);r&&i&&(r.isWrapped=i[s.CHAR_DATA_CODE_INDEX]!==s.NULL_CELL_CODE&&i[s.CHAR_DATA_CODE_INDEX]!==s.WHITESPACE_CELL_CODE)}},3734:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.ExtendedAttrs=t.AttributeData=void 0;class i{constructor(){this.fg=0,this.bg=0,this.extended=new s}static toColorRGB(e){return[e>>>16&255,e>>>8&255,255&e]}static fromColorRGB(e){return(255&e[0])<<16|(255&e[1])<<8|255&e[2]}clone(){const e=new i;return e.fg=this.fg,e.bg=this.bg,e.extended=this.extended.clone(),e}isInverse(){return 67108864&this.fg}isBold(){return 134217728&this.fg}isUnderline(){return this.hasExtendedAttrs()&&0!==this.extended.underlineStyle?1:268435456&this.fg}isBlink(){return 536870912&this.fg}isInvisible(){return 1073741824&this.fg}isItalic(){return 67108864&this.bg}isDim(){return 134217728&this.bg}isStrikethrough(){return 2147483648&this.fg}isProtected(){return 536870912&this.bg}isOverline(){return 1073741824&this.bg}getFgColorMode(){return 50331648&this.fg}getBgColorMode(){return 50331648&this.bg}isFgRGB(){return 50331648==(50331648&this.fg)}isBgRGB(){return 50331648==(50331648&this.bg)}isFgPalette(){return 16777216==(50331648&this.fg)||33554432==(50331648&this.fg)}isBgPalette(){return 16777216==(50331648&this.bg)||33554432==(50331648&this.bg)}isFgDefault(){return 0==(50331648&this.fg)}isBgDefault(){return 0==(50331648&this.bg)}isAttributeDefault(){return 0===this.fg&&0===this.bg}getFgColor(){switch(50331648&this.fg){case 16777216:case 33554432:return 255&this.fg;case 50331648:return 16777215&this.fg;default:return-1}}getBgColor(){switch(50331648&this.bg){case 16777216:case 33554432:return 255&this.bg;case 50331648:return 16777215&this.bg;default:return-1}}hasExtendedAttrs(){return 268435456&this.bg}updateExtended(){this.extended.isEmpty()?this.bg&=-268435457:this.bg|=268435456}getUnderlineColor(){if(268435456&this.bg&&~this.extended.underlineColor)switch(50331648&this.extended.underlineColor){case 16777216:case 33554432:return 255&this.extended.underlineColor;case 50331648:return 16777215&this.extended.underlineColor;default:return this.getFgColor()}return this.getFgColor()}getUnderlineColorMode(){return 268435456&this.bg&&~this.extended.underlineColor?50331648&this.extended.underlineColor:this.getFgColorMode()}isUnderlineColorRGB(){return 268435456&this.bg&&~this.extended.underlineColor?50331648==(50331648&this.extended.underlineColor):this.isFgRGB()}isUnderlineColorPalette(){return 268435456&this.bg&&~this.extended.underlineColor?16777216==(50331648&this.extended.underlineColor)||33554432==(50331648&this.extended.underlineColor):this.isFgPalette()}isUnderlineColorDefault(){return 268435456&this.bg&&~this.extended.underlineColor?0==(50331648&this.extended.underlineColor):this.isFgDefault()}getUnderlineStyle(){return 268435456&this.fg?268435456&this.bg?this.extended.underlineStyle:1:0}}t.AttributeData=i;class s{get ext(){return this._urlId?-469762049&this._ext|this.underlineStyle<<26:this._ext}set ext(e){this._ext=e}get underlineStyle(){return this._urlId?5:(469762048&this._ext)>>26}set underlineStyle(e){this._ext&=-469762049,this._ext|=e<<26&469762048}get underlineColor(){return 67108863&this._ext}set underlineColor(e){this._ext&=-67108864,this._ext|=67108863&e}get urlId(){return this._urlId}set urlId(e){this._urlId=e}constructor(e=0,t=0){this._ext=0,this._urlId=0,this._ext=e,this._urlId=t}clone(){return new s(this._ext,this._urlId)}isEmpty(){return 0===this.underlineStyle&&0===this._urlId}}t.ExtendedAttrs=s},9092:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.Buffer=t.MAX_BUFFER_SIZE=void 0;const s=i(6349),r=i(7226),n=i(3734),o=i(8437),a=i(4634),h=i(511),c=i(643),l=i(4863),d=i(7116);t.MAX_BUFFER_SIZE=4294967295,t.Buffer=class{constructor(e,t,i){this._hasScrollback=e,this._optionsService=t,this._bufferService=i,this.ydisp=0,this.ybase=0,this.y=0,this.x=0,this.tabs={},this.savedY=0,this.savedX=0,this.savedCurAttrData=o.DEFAULT_ATTR_DATA.clone(),this.savedCharset=d.DEFAULT_CHARSET,this.markers=[],this._nullCell=h.CellData.fromCharData([0,c.NULL_CELL_CHAR,c.NULL_CELL_WIDTH,c.NULL_CELL_CODE]),this._whitespaceCell=h.CellData.fromCharData([0,c.WHITESPACE_CELL_CHAR,c.WHITESPACE_CELL_WIDTH,c.WHITESPACE_CELL_CODE]),this._isClearing=!1,this._memoryCleanupQueue=new r.IdleTaskQueue,this._memoryCleanupPosition=0,this._cols=this._bufferService.cols,this._rows=this._bufferService.rows,this.lines=new s.CircularList(this._getCorrectBufferLength(this._rows)),this.scrollTop=0,this.scrollBottom=this._rows-1,this.setupTabStops()}getNullCell(e){return e?(this._nullCell.fg=e.fg,this._nullCell.bg=e.bg,this._nullCell.extended=e.extended):(this._nullCell.fg=0,this._nullCell.bg=0,this._nullCell.extended=new n.ExtendedAttrs),this._nullCell}getWhitespaceCell(e){return e?(this._whitespaceCell.fg=e.fg,this._whitespaceCell.bg=e.bg,this._whitespaceCell.extended=e.extended):(this._whitespaceCell.fg=0,this._whitespaceCell.bg=0,this._whitespaceCell.extended=new n.ExtendedAttrs),this._whitespaceCell}getBlankLine(e,t){return new o.BufferLine(this._bufferService.cols,this.getNullCell(e),t)}get hasScrollback(){return this._hasScrollback&&this.lines.maxLength>this._rows}get isCursorInViewport(){const e=this.ybase+this.y-this.ydisp;return e>=0&&et.MAX_BUFFER_SIZE?t.MAX_BUFFER_SIZE:i}fillViewportRows(e){if(0===this.lines.length){void 0===e&&(e=o.DEFAULT_ATTR_DATA);let t=this._rows;for(;t--;)this.lines.push(this.getBlankLine(e))}}clear(){this.ydisp=0,this.ybase=0,this.y=0,this.x=0,this.lines=new s.CircularList(this._getCorrectBufferLength(this._rows)),this.scrollTop=0,this.scrollBottom=this._rows-1,this.setupTabStops()}resize(e,t){const i=this.getNullCell(o.DEFAULT_ATTR_DATA);let s=0;const r=this._getCorrectBufferLength(t);if(r>this.lines.maxLength&&(this.lines.maxLength=r),this.lines.length>0){if(this._cols0&&this.lines.length<=this.ybase+this.y+n+1?(this.ybase--,n++,this.ydisp>0&&this.ydisp--):this.lines.push(new o.BufferLine(e,i)));else for(let e=this._rows;e>t;e--)this.lines.length>t+this.ybase&&(this.lines.length>this.ybase+this.y+1?this.lines.pop():(this.ybase++,this.ydisp++));if(r0&&(this.lines.trimStart(e),this.ybase=Math.max(this.ybase-e,0),this.ydisp=Math.max(this.ydisp-e,0),this.savedY=Math.max(this.savedY-e,0)),this.lines.maxLength=r}this.x=Math.min(this.x,e-1),this.y=Math.min(this.y,t-1),n&&(this.y+=n),this.savedX=Math.min(this.savedX,e-1),this.scrollTop=0}if(this.scrollBottom=t-1,this._isReflowEnabled&&(this._reflow(e,t),this._cols>e))for(let t=0;t.1*this.lines.length&&(this._memoryCleanupPosition=0,this._memoryCleanupQueue.enqueue((()=>this._batchedMemoryCleanup())))}_batchedMemoryCleanup(){let e=!0;this._memoryCleanupPosition>=this.lines.length&&(this._memoryCleanupPosition=0,e=!1);let t=0;for(;this._memoryCleanupPosition100)return!0;return e}get _isReflowEnabled(){const e=this._optionsService.rawOptions.windowsPty;return e&&e.buildNumber?this._hasScrollback&&"conpty"===e.backend&&e.buildNumber>=21376:this._hasScrollback&&!this._optionsService.rawOptions.windowsMode}_reflow(e,t){this._cols!==e&&(e>this._cols?this._reflowLarger(e,t):this._reflowSmaller(e,t))}_reflowLarger(e,t){const i=(0,a.reflowLargerGetLinesToRemove)(this.lines,this._cols,e,this.ybase+this.y,this.getNullCell(o.DEFAULT_ATTR_DATA));if(i.length>0){const s=(0,a.reflowLargerCreateNewLayout)(this.lines,i);(0,a.reflowLargerApplyNewLayout)(this.lines,s.layout),this._reflowLargerAdjustViewport(e,t,s.countRemoved)}}_reflowLargerAdjustViewport(e,t,i){const s=this.getNullCell(o.DEFAULT_ATTR_DATA);let r=i;for(;r-- >0;)0===this.ybase?(this.y>0&&this.y--,this.lines.length=0;n--){let h=this.lines.get(n);if(!h||!h.isWrapped&&h.getTrimmedLength()<=e)continue;const c=[h];for(;h.isWrapped&&n>0;)h=this.lines.get(--n),c.unshift(h);const l=this.ybase+this.y;if(l>=n&&l0&&(s.push({start:n+c.length+r,newLines:v}),r+=v.length),c.push(...v);let p=_.length-1,g=_[p];0===g&&(p--,g=_[p]);let m=c.length-u-1,S=d;for(;m>=0;){const e=Math.min(S,g);if(void 0===c[p])break;if(c[p].copyCellsFrom(c[m],S-e,g-e,e,!0),g-=e,0===g&&(p--,g=_[p]),S-=e,0===S){m--;const e=Math.max(m,0);S=(0,a.getWrappedLineTrimmedLength)(c,e,this._cols)}}for(let t=0;t0;)0===this.ybase?this.y0){const e=[],t=[];for(let e=0;e=0;c--)if(a&&a.start>n+h){for(let e=a.newLines.length-1;e>=0;e--)this.lines.set(c--,a.newLines[e]);c++,e.push({index:n+1,amount:a.newLines.length}),h+=a.newLines.length,a=s[++o]}else this.lines.set(c,t[n--]);let c=0;for(let t=e.length-1;t>=0;t--)e[t].index+=c,this.lines.onInsertEmitter.fire(e[t]),c+=e[t].amount;const l=Math.max(0,i+r-this.lines.maxLength);l>0&&this.lines.onTrimEmitter.fire(l)}}translateBufferLineToString(e,t,i=0,s){const r=this.lines.get(e);return r?r.translateToString(t,i,s):""}getWrappedRangeForLine(e){let t=e,i=e;for(;t>0&&this.lines.get(t).isWrapped;)t--;for(;i+10;);return e>=this._cols?this._cols-1:e<0?0:e}nextStop(e){for(null==e&&(e=this.x);!this.tabs[++e]&&e=this._cols?this._cols-1:e<0?0:e}clearMarkers(e){this._isClearing=!0;for(let t=0;t{t.line-=e,t.line<0&&t.dispose()}))),t.register(this.lines.onInsert((e=>{t.line>=e.index&&(t.line+=e.amount)}))),t.register(this.lines.onDelete((e=>{t.line>=e.index&&t.linee.index&&(t.line-=e.amount)}))),t.register(t.onDispose((()=>this._removeMarker(t)))),t}_removeMarker(e){this._isClearing||this.markers.splice(this.markers.indexOf(e),1)}}},8437:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.BufferLine=t.DEFAULT_ATTR_DATA=void 0;const s=i(3734),r=i(511),n=i(643),o=i(482);t.DEFAULT_ATTR_DATA=Object.freeze(new s.AttributeData);let a=0;class h{constructor(e,t,i=!1){this.isWrapped=i,this._combined={},this._extendedAttrs={},this._data=new Uint32Array(3*e);const s=t||r.CellData.fromCharData([0,n.NULL_CELL_CHAR,n.NULL_CELL_WIDTH,n.NULL_CELL_CODE]);for(let t=0;t>22,2097152&t?this._combined[e].charCodeAt(this._combined[e].length-1):i]}set(e,t){this._data[3*e+1]=t[n.CHAR_DATA_ATTR_INDEX],t[n.CHAR_DATA_CHAR_INDEX].length>1?(this._combined[e]=t[1],this._data[3*e+0]=2097152|e|t[n.CHAR_DATA_WIDTH_INDEX]<<22):this._data[3*e+0]=t[n.CHAR_DATA_CHAR_INDEX].charCodeAt(0)|t[n.CHAR_DATA_WIDTH_INDEX]<<22}getWidth(e){return this._data[3*e+0]>>22}hasWidth(e){return 12582912&this._data[3*e+0]}getFg(e){return this._data[3*e+1]}getBg(e){return this._data[3*e+2]}hasContent(e){return 4194303&this._data[3*e+0]}getCodePoint(e){const t=this._data[3*e+0];return 2097152&t?this._combined[e].charCodeAt(this._combined[e].length-1):2097151&t}isCombined(e){return 2097152&this._data[3*e+0]}getString(e){const t=this._data[3*e+0];return 2097152&t?this._combined[e]:2097151&t?(0,o.stringFromCodePoint)(2097151&t):""}isProtected(e){return 536870912&this._data[3*e+2]}loadCell(e,t){return a=3*e,t.content=this._data[a+0],t.fg=this._data[a+1],t.bg=this._data[a+2],2097152&t.content&&(t.combinedData=this._combined[e]),268435456&t.bg&&(t.extended=this._extendedAttrs[e]),t}setCell(e,t){2097152&t.content&&(this._combined[e]=t.combinedData),268435456&t.bg&&(this._extendedAttrs[e]=t.extended),this._data[3*e+0]=t.content,this._data[3*e+1]=t.fg,this._data[3*e+2]=t.bg}setCellFromCodePoint(e,t,i,s,r,n){268435456&r&&(this._extendedAttrs[e]=n),this._data[3*e+0]=t|i<<22,this._data[3*e+1]=s,this._data[3*e+2]=r}addCodepointToCell(e,t){let i=this._data[3*e+0];2097152&i?this._combined[e]+=(0,o.stringFromCodePoint)(t):(2097151&i?(this._combined[e]=(0,o.stringFromCodePoint)(2097151&i)+(0,o.stringFromCodePoint)(t),i&=-2097152,i|=2097152):i=t|1<<22,this._data[3*e+0]=i)}insertCells(e,t,i,n){if((e%=this.length)&&2===this.getWidth(e-1)&&this.setCellFromCodePoint(e-1,0,1,(null==n?void 0:n.fg)||0,(null==n?void 0:n.bg)||0,(null==n?void 0:n.extended)||new s.ExtendedAttrs),t=0;--i)this.setCell(e+t+i,this.loadCell(e+i,s));for(let s=0;sthis.length){if(this._data.buffer.byteLength>=4*i)this._data=new Uint32Array(this._data.buffer,0,i);else{const e=new Uint32Array(i);e.set(this._data),this._data=e}for(let i=this.length;i=e&&delete this._combined[s]}const s=Object.keys(this._extendedAttrs);for(let t=0;t=e&&delete this._extendedAttrs[i]}}return this.length=e,4*i*2=0;--e)if(4194303&this._data[3*e+0])return e+(this._data[3*e+0]>>22);return 0}getNoBgTrimmedLength(){for(let e=this.length-1;e>=0;--e)if(4194303&this._data[3*e+0]||50331648&this._data[3*e+2])return e+(this._data[3*e+0]>>22);return 0}copyCellsFrom(e,t,i,s,r){const n=e._data;if(r)for(let r=s-1;r>=0;r--){for(let e=0;e<3;e++)this._data[3*(i+r)+e]=n[3*(t+r)+e];268435456&n[3*(t+r)+2]&&(this._extendedAttrs[i+r]=e._extendedAttrs[t+r])}else for(let r=0;r=t&&(this._combined[r-t+i]=e._combined[r])}}translateToString(e=!1,t=0,i=this.length){e&&(i=Math.min(i,this.getTrimmedLength()));let s="";for(;t>22||1}return s}}t.BufferLine=h},4841:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.getRangeLength=void 0,t.getRangeLength=function(e,t){if(e.start.y>e.end.y)throw new Error(`Buffer range end (${e.end.x}, ${e.end.y}) cannot be before start (${e.start.x}, ${e.start.y})`);return t*(e.end.y-e.start.y)+(e.end.x-e.start.x+1)}},4634:(e,t)=>{function i(e,t,i){if(t===e.length-1)return e[t].getTrimmedLength();const s=!e[t].hasContent(i-1)&&1===e[t].getWidth(i-1),r=2===e[t+1].getWidth(0);return s&&r?i-1:i}Object.defineProperty(t,"__esModule",{value:!0}),t.getWrappedLineTrimmedLength=t.reflowSmallerGetNewLineLengths=t.reflowLargerApplyNewLayout=t.reflowLargerCreateNewLayout=t.reflowLargerGetLinesToRemove=void 0,t.reflowLargerGetLinesToRemove=function(e,t,s,r,n){const o=[];for(let a=0;a=a&&r0&&(e>d||0===l[e].getTrimmedLength());e--)v++;v>0&&(o.push(a+l.length-v),o.push(v)),a+=l.length-1}return o},t.reflowLargerCreateNewLayout=function(e,t){const i=[];let s=0,r=t[s],n=0;for(let o=0;oi(e,r,t))).reduce(((e,t)=>e+t));let o=0,a=0,h=0;for(;hc&&(o-=c,a++);const l=2===e[a].getWidth(o-1);l&&o--;const d=l?s-1:s;r.push(d),h+=d}return r},t.getWrappedLineTrimmedLength=i},5295:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.BufferSet=void 0;const s=i(8460),r=i(844),n=i(9092);class o extends r.Disposable{constructor(e,t){super(),this._optionsService=e,this._bufferService=t,this._onBufferActivate=this.register(new s.EventEmitter),this.onBufferActivate=this._onBufferActivate.event,this.reset(),this.register(this._optionsService.onSpecificOptionChange("scrollback",(()=>this.resize(this._bufferService.cols,this._bufferService.rows)))),this.register(this._optionsService.onSpecificOptionChange("tabStopWidth",(()=>this.setupTabStops())))}reset(){this._normal=new n.Buffer(!0,this._optionsService,this._bufferService),this._normal.fillViewportRows(),this._alt=new n.Buffer(!1,this._optionsService,this._bufferService),this._activeBuffer=this._normal,this._onBufferActivate.fire({activeBuffer:this._normal,inactiveBuffer:this._alt}),this.setupTabStops()}get alt(){return this._alt}get active(){return this._activeBuffer}get normal(){return this._normal}activateNormalBuffer(){this._activeBuffer!==this._normal&&(this._normal.x=this._alt.x,this._normal.y=this._alt.y,this._alt.clearAllMarkers(),this._alt.clear(),this._activeBuffer=this._normal,this._onBufferActivate.fire({activeBuffer:this._normal,inactiveBuffer:this._alt}))}activateAltBuffer(e){this._activeBuffer!==this._alt&&(this._alt.fillViewportRows(e),this._alt.x=this._normal.x,this._alt.y=this._normal.y,this._activeBuffer=this._alt,this._onBufferActivate.fire({activeBuffer:this._alt,inactiveBuffer:this._normal}))}resize(e,t){this._normal.resize(e,t),this._alt.resize(e,t),this.setupTabStops(e)}setupTabStops(e){this._normal.setupTabStops(e),this._alt.setupTabStops(e)}}t.BufferSet=o},511:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.CellData=void 0;const s=i(482),r=i(643),n=i(3734);class o extends n.AttributeData{constructor(){super(...arguments),this.content=0,this.fg=0,this.bg=0,this.extended=new n.ExtendedAttrs,this.combinedData=""}static fromCharData(e){const t=new o;return t.setFromCharData(e),t}isCombined(){return 2097152&this.content}getWidth(){return this.content>>22}getChars(){return 2097152&this.content?this.combinedData:2097151&this.content?(0,s.stringFromCodePoint)(2097151&this.content):""}getCode(){return this.isCombined()?this.combinedData.charCodeAt(this.combinedData.length-1):2097151&this.content}setFromCharData(e){this.fg=e[r.CHAR_DATA_ATTR_INDEX],this.bg=0;let t=!1;if(e[r.CHAR_DATA_CHAR_INDEX].length>2)t=!0;else if(2===e[r.CHAR_DATA_CHAR_INDEX].length){const i=e[r.CHAR_DATA_CHAR_INDEX].charCodeAt(0);if(55296<=i&&i<=56319){const s=e[r.CHAR_DATA_CHAR_INDEX].charCodeAt(1);56320<=s&&s<=57343?this.content=1024*(i-55296)+s-56320+65536|e[r.CHAR_DATA_WIDTH_INDEX]<<22:t=!0}else t=!0}else this.content=e[r.CHAR_DATA_CHAR_INDEX].charCodeAt(0)|e[r.CHAR_DATA_WIDTH_INDEX]<<22;t&&(this.combinedData=e[r.CHAR_DATA_CHAR_INDEX],this.content=2097152|e[r.CHAR_DATA_WIDTH_INDEX]<<22)}getAsCharData(){return[this.fg,this.getChars(),this.getWidth(),this.getCode()]}}t.CellData=o},643:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.WHITESPACE_CELL_CODE=t.WHITESPACE_CELL_WIDTH=t.WHITESPACE_CELL_CHAR=t.NULL_CELL_CODE=t.NULL_CELL_WIDTH=t.NULL_CELL_CHAR=t.CHAR_DATA_CODE_INDEX=t.CHAR_DATA_WIDTH_INDEX=t.CHAR_DATA_CHAR_INDEX=t.CHAR_DATA_ATTR_INDEX=t.DEFAULT_EXT=t.DEFAULT_ATTR=t.DEFAULT_COLOR=void 0,t.DEFAULT_COLOR=0,t.DEFAULT_ATTR=256|t.DEFAULT_COLOR<<9,t.DEFAULT_EXT=0,t.CHAR_DATA_ATTR_INDEX=0,t.CHAR_DATA_CHAR_INDEX=1,t.CHAR_DATA_WIDTH_INDEX=2,t.CHAR_DATA_CODE_INDEX=3,t.NULL_CELL_CHAR="",t.NULL_CELL_WIDTH=1,t.NULL_CELL_CODE=0,t.WHITESPACE_CELL_CHAR=" ",t.WHITESPACE_CELL_WIDTH=1,t.WHITESPACE_CELL_CODE=32},4863:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.Marker=void 0;const s=i(8460),r=i(844);class n{get id(){return this._id}constructor(e){this.line=e,this.isDisposed=!1,this._disposables=[],this._id=n._nextId++,this._onDispose=this.register(new s.EventEmitter),this.onDispose=this._onDispose.event}dispose(){this.isDisposed||(this.isDisposed=!0,this.line=-1,this._onDispose.fire(),(0,r.disposeArray)(this._disposables),this._disposables.length=0)}register(e){return this._disposables.push(e),e}}t.Marker=n,n._nextId=1},7116:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.DEFAULT_CHARSET=t.CHARSETS=void 0,t.CHARSETS={},t.DEFAULT_CHARSET=t.CHARSETS.B,t.CHARSETS[0]={"`":"◆",a:"▒",b:"␉",c:"␌",d:"␍",e:"␊",f:"°",g:"±",h:"␤",i:"␋",j:"┘",k:"┐",l:"┌",m:"└",n:"┼",o:"⎺",p:"⎻",q:"─",r:"⎼",s:"⎽",t:"├",u:"┤",v:"┴",w:"┬",x:"│",y:"≤",z:"≥","{":"π","|":"≠","}":"£","~":"·"},t.CHARSETS.A={"#":"£"},t.CHARSETS.B=void 0,t.CHARSETS[4]={"#":"£","@":"¾","[":"ij","\\":"½","]":"|","{":"¨","|":"f","}":"¼","~":"´"},t.CHARSETS.C=t.CHARSETS[5]={"[":"Ä","\\":"Ö","]":"Å","^":"Ü","`":"é","{":"ä","|":"ö","}":"å","~":"ü"},t.CHARSETS.R={"#":"£","@":"à","[":"°","\\":"ç","]":"§","{":"é","|":"ù","}":"è","~":"¨"},t.CHARSETS.Q={"@":"à","[":"â","\\":"ç","]":"ê","^":"î","`":"ô","{":"é","|":"ù","}":"è","~":"û"},t.CHARSETS.K={"@":"§","[":"Ä","\\":"Ö","]":"Ü","{":"ä","|":"ö","}":"ü","~":"ß"},t.CHARSETS.Y={"#":"£","@":"§","[":"°","\\":"ç","]":"é","`":"ù","{":"à","|":"ò","}":"è","~":"ì"},t.CHARSETS.E=t.CHARSETS[6]={"@":"Ä","[":"Æ","\\":"Ø","]":"Å","^":"Ü","`":"ä","{":"æ","|":"ø","}":"å","~":"ü"},t.CHARSETS.Z={"#":"£","@":"§","[":"¡","\\":"Ñ","]":"¿","{":"°","|":"ñ","}":"ç"},t.CHARSETS.H=t.CHARSETS[7]={"@":"É","[":"Ä","\\":"Ö","]":"Å","^":"Ü","`":"é","{":"ä","|":"ö","}":"å","~":"ü"},t.CHARSETS["="]={"#":"ù","@":"à","[":"é","\\":"ç","]":"ê","^":"î",_:"è","`":"ô","{":"ä","|":"ö","}":"ü","~":"û"}},2584:(e,t)=>{var i,s,r;Object.defineProperty(t,"__esModule",{value:!0}),t.C1_ESCAPED=t.C1=t.C0=void 0,function(e){e.NUL="\0",e.SOH="",e.STX="",e.ETX="",e.EOT="",e.ENQ="",e.ACK="",e.BEL="",e.BS="\b",e.HT="\t",e.LF="\n",e.VT="\v",e.FF="\f",e.CR="\r",e.SO="",e.SI="",e.DLE="",e.DC1="",e.DC2="",e.DC3="",e.DC4="",e.NAK="",e.SYN="",e.ETB="",e.CAN="",e.EM="",e.SUB="",e.ESC="",e.FS="",e.GS="",e.RS="",e.US="",e.SP=" ",e.DEL=""}(i||(t.C0=i={})),function(e){e.PAD="€",e.HOP="",e.BPH="‚",e.NBH="ƒ",e.IND="„",e.NEL="…",e.SSA="†",e.ESA="‡",e.HTS="ˆ",e.HTJ="‰",e.VTS="Š",e.PLD="‹",e.PLU="Œ",e.RI="",e.SS2="Ž",e.SS3="",e.DCS="",e.PU1="‘",e.PU2="’",e.STS="“",e.CCH="”",e.MW="•",e.SPA="–",e.EPA="—",e.SOS="˜",e.SGCI="™",e.SCI="š",e.CSI="›",e.ST="œ",e.OSC="",e.PM="ž",e.APC="Ÿ"}(s||(t.C1=s={})),function(e){e.ST=`${i.ESC}\\`}(r||(t.C1_ESCAPED=r={}))},7399:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.evaluateKeyboardEvent=void 0;const s=i(2584),r={48:["0",")"],49:["1","!"],50:["2","@"],51:["3","#"],52:["4","$"],53:["5","%"],54:["6","^"],55:["7","&"],56:["8","*"],57:["9","("],186:[";",":"],187:["=","+"],188:[",","<"],189:["-","_"],190:[".",">"],191:["/","?"],192:["`","~"],219:["[","{"],220:["\\","|"],221:["]","}"],222:["'",'"']};t.evaluateKeyboardEvent=function(e,t,i,n){const o={type:0,cancel:!1,key:void 0},a=(e.shiftKey?1:0)|(e.altKey?2:0)|(e.ctrlKey?4:0)|(e.metaKey?8:0);switch(e.keyCode){case 0:"UIKeyInputUpArrow"===e.key?o.key=t?s.C0.ESC+"OA":s.C0.ESC+"[A":"UIKeyInputLeftArrow"===e.key?o.key=t?s.C0.ESC+"OD":s.C0.ESC+"[D":"UIKeyInputRightArrow"===e.key?o.key=t?s.C0.ESC+"OC":s.C0.ESC+"[C":"UIKeyInputDownArrow"===e.key&&(o.key=t?s.C0.ESC+"OB":s.C0.ESC+"[B");break;case 8:if(e.altKey){o.key=s.C0.ESC+s.C0.DEL;break}o.key=s.C0.DEL;break;case 9:if(e.shiftKey){o.key=s.C0.ESC+"[Z";break}o.key=s.C0.HT,o.cancel=!0;break;case 13:o.key=e.altKey?s.C0.ESC+s.C0.CR:s.C0.CR,o.cancel=!0;break;case 27:o.key=s.C0.ESC,e.altKey&&(o.key=s.C0.ESC+s.C0.ESC),o.cancel=!0;break;case 37:if(e.metaKey)break;a?(o.key=s.C0.ESC+"[1;"+(a+1)+"D",o.key===s.C0.ESC+"[1;3D"&&(o.key=s.C0.ESC+(i?"b":"[1;5D"))):o.key=t?s.C0.ESC+"OD":s.C0.ESC+"[D";break;case 39:if(e.metaKey)break;a?(o.key=s.C0.ESC+"[1;"+(a+1)+"C",o.key===s.C0.ESC+"[1;3C"&&(o.key=s.C0.ESC+(i?"f":"[1;5C"))):o.key=t?s.C0.ESC+"OC":s.C0.ESC+"[C";break;case 38:if(e.metaKey)break;a?(o.key=s.C0.ESC+"[1;"+(a+1)+"A",i||o.key!==s.C0.ESC+"[1;3A"||(o.key=s.C0.ESC+"[1;5A")):o.key=t?s.C0.ESC+"OA":s.C0.ESC+"[A";break;case 40:if(e.metaKey)break;a?(o.key=s.C0.ESC+"[1;"+(a+1)+"B",i||o.key!==s.C0.ESC+"[1;3B"||(o.key=s.C0.ESC+"[1;5B")):o.key=t?s.C0.ESC+"OB":s.C0.ESC+"[B";break;case 45:e.shiftKey||e.ctrlKey||(o.key=s.C0.ESC+"[2~");break;case 46:o.key=a?s.C0.ESC+"[3;"+(a+1)+"~":s.C0.ESC+"[3~";break;case 36:o.key=a?s.C0.ESC+"[1;"+(a+1)+"H":t?s.C0.ESC+"OH":s.C0.ESC+"[H";break;case 35:o.key=a?s.C0.ESC+"[1;"+(a+1)+"F":t?s.C0.ESC+"OF":s.C0.ESC+"[F";break;case 33:e.shiftKey?o.type=2:e.ctrlKey?o.key=s.C0.ESC+"[5;"+(a+1)+"~":o.key=s.C0.ESC+"[5~";break;case 34:e.shiftKey?o.type=3:e.ctrlKey?o.key=s.C0.ESC+"[6;"+(a+1)+"~":o.key=s.C0.ESC+"[6~";break;case 112:o.key=a?s.C0.ESC+"[1;"+(a+1)+"P":s.C0.ESC+"OP";break;case 113:o.key=a?s.C0.ESC+"[1;"+(a+1)+"Q":s.C0.ESC+"OQ";break;case 114:o.key=a?s.C0.ESC+"[1;"+(a+1)+"R":s.C0.ESC+"OR";break;case 115:o.key=a?s.C0.ESC+"[1;"+(a+1)+"S":s.C0.ESC+"OS";break;case 116:o.key=a?s.C0.ESC+"[15;"+(a+1)+"~":s.C0.ESC+"[15~";break;case 117:o.key=a?s.C0.ESC+"[17;"+(a+1)+"~":s.C0.ESC+"[17~";break;case 118:o.key=a?s.C0.ESC+"[18;"+(a+1)+"~":s.C0.ESC+"[18~";break;case 119:o.key=a?s.C0.ESC+"[19;"+(a+1)+"~":s.C0.ESC+"[19~";break;case 120:o.key=a?s.C0.ESC+"[20;"+(a+1)+"~":s.C0.ESC+"[20~";break;case 121:o.key=a?s.C0.ESC+"[21;"+(a+1)+"~":s.C0.ESC+"[21~";break;case 122:o.key=a?s.C0.ESC+"[23;"+(a+1)+"~":s.C0.ESC+"[23~";break;case 123:o.key=a?s.C0.ESC+"[24;"+(a+1)+"~":s.C0.ESC+"[24~";break;default:if(!e.ctrlKey||e.shiftKey||e.altKey||e.metaKey)if(i&&!n||!e.altKey||e.metaKey)!i||e.altKey||e.ctrlKey||e.shiftKey||!e.metaKey?e.key&&!e.ctrlKey&&!e.altKey&&!e.metaKey&&e.keyCode>=48&&1===e.key.length?o.key=e.key:e.key&&e.ctrlKey&&("_"===e.key&&(o.key=s.C0.US),"@"===e.key&&(o.key=s.C0.NUL)):65===e.keyCode&&(o.type=1);else{const t=r[e.keyCode],i=null==t?void 0:t[e.shiftKey?1:0];if(i)o.key=s.C0.ESC+i;else if(e.keyCode>=65&&e.keyCode<=90){const t=e.ctrlKey?e.keyCode-64:e.keyCode+32;let i=String.fromCharCode(t);e.shiftKey&&(i=i.toUpperCase()),o.key=s.C0.ESC+i}else if(32===e.keyCode)o.key=s.C0.ESC+(e.ctrlKey?s.C0.NUL:" ");else if("Dead"===e.key&&e.code.startsWith("Key")){let t=e.code.slice(3,4);e.shiftKey||(t=t.toLowerCase()),o.key=s.C0.ESC+t,o.cancel=!0}}else e.keyCode>=65&&e.keyCode<=90?o.key=String.fromCharCode(e.keyCode-64):32===e.keyCode?o.key=s.C0.NUL:e.keyCode>=51&&e.keyCode<=55?o.key=String.fromCharCode(e.keyCode-51+27):56===e.keyCode?o.key=s.C0.DEL:219===e.keyCode?o.key=s.C0.ESC:220===e.keyCode?o.key=s.C0.FS:221===e.keyCode&&(o.key=s.C0.GS)}return o}},482:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.Utf8ToUtf32=t.StringToUtf32=t.utf32ToString=t.stringFromCodePoint=void 0,t.stringFromCodePoint=function(e){return e>65535?(e-=65536,String.fromCharCode(55296+(e>>10))+String.fromCharCode(e%1024+56320)):String.fromCharCode(e)},t.utf32ToString=function(e,t=0,i=e.length){let s="";for(let r=t;r65535?(t-=65536,s+=String.fromCharCode(55296+(t>>10))+String.fromCharCode(t%1024+56320)):s+=String.fromCharCode(t)}return s},t.StringToUtf32=class{constructor(){this._interim=0}clear(){this._interim=0}decode(e,t){const i=e.length;if(!i)return 0;let s=0,r=0;if(this._interim){const i=e.charCodeAt(r++);56320<=i&&i<=57343?t[s++]=1024*(this._interim-55296)+i-56320+65536:(t[s++]=this._interim,t[s++]=i),this._interim=0}for(let n=r;n=i)return this._interim=r,s;const o=e.charCodeAt(n);56320<=o&&o<=57343?t[s++]=1024*(r-55296)+o-56320+65536:(t[s++]=r,t[s++]=o)}else 65279!==r&&(t[s++]=r)}return s}},t.Utf8ToUtf32=class{constructor(){this.interim=new Uint8Array(3)}clear(){this.interim.fill(0)}decode(e,t){const i=e.length;if(!i)return 0;let s,r,n,o,a=0,h=0,c=0;if(this.interim[0]){let s=!1,r=this.interim[0];r&=192==(224&r)?31:224==(240&r)?15:7;let n,o=0;for(;(n=63&this.interim[++o])&&o<4;)r<<=6,r|=n;const h=192==(224&this.interim[0])?2:224==(240&this.interim[0])?3:4,l=h-o;for(;c=i)return 0;if(n=e[c++],128!=(192&n)){c--,s=!0;break}this.interim[o++]=n,r<<=6,r|=63&n}s||(2===h?r<128?c--:t[a++]=r:3===h?r<2048||r>=55296&&r<=57343||65279===r||(t[a++]=r):r<65536||r>1114111||(t[a++]=r)),this.interim.fill(0)}const l=i-4;let d=c;for(;d=i)return this.interim[0]=s,a;if(r=e[d++],128!=(192&r)){d--;continue}if(h=(31&s)<<6|63&r,h<128){d--;continue}t[a++]=h}else if(224==(240&s)){if(d>=i)return this.interim[0]=s,a;if(r=e[d++],128!=(192&r)){d--;continue}if(d>=i)return this.interim[0]=s,this.interim[1]=r,a;if(n=e[d++],128!=(192&n)){d--;continue}if(h=(15&s)<<12|(63&r)<<6|63&n,h<2048||h>=55296&&h<=57343||65279===h)continue;t[a++]=h}else if(240==(248&s)){if(d>=i)return this.interim[0]=s,a;if(r=e[d++],128!=(192&r)){d--;continue}if(d>=i)return this.interim[0]=s,this.interim[1]=r,a;if(n=e[d++],128!=(192&n)){d--;continue}if(d>=i)return this.interim[0]=s,this.interim[1]=r,this.interim[2]=n,a;if(o=e[d++],128!=(192&o)){d--;continue}if(h=(7&s)<<18|(63&r)<<12|(63&n)<<6|63&o,h<65536||h>1114111)continue;t[a++]=h}}return a}}},225:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.UnicodeV6=void 0;const i=[[768,879],[1155,1158],[1160,1161],[1425,1469],[1471,1471],[1473,1474],[1476,1477],[1479,1479],[1536,1539],[1552,1557],[1611,1630],[1648,1648],[1750,1764],[1767,1768],[1770,1773],[1807,1807],[1809,1809],[1840,1866],[1958,1968],[2027,2035],[2305,2306],[2364,2364],[2369,2376],[2381,2381],[2385,2388],[2402,2403],[2433,2433],[2492,2492],[2497,2500],[2509,2509],[2530,2531],[2561,2562],[2620,2620],[2625,2626],[2631,2632],[2635,2637],[2672,2673],[2689,2690],[2748,2748],[2753,2757],[2759,2760],[2765,2765],[2786,2787],[2817,2817],[2876,2876],[2879,2879],[2881,2883],[2893,2893],[2902,2902],[2946,2946],[3008,3008],[3021,3021],[3134,3136],[3142,3144],[3146,3149],[3157,3158],[3260,3260],[3263,3263],[3270,3270],[3276,3277],[3298,3299],[3393,3395],[3405,3405],[3530,3530],[3538,3540],[3542,3542],[3633,3633],[3636,3642],[3655,3662],[3761,3761],[3764,3769],[3771,3772],[3784,3789],[3864,3865],[3893,3893],[3895,3895],[3897,3897],[3953,3966],[3968,3972],[3974,3975],[3984,3991],[3993,4028],[4038,4038],[4141,4144],[4146,4146],[4150,4151],[4153,4153],[4184,4185],[4448,4607],[4959,4959],[5906,5908],[5938,5940],[5970,5971],[6002,6003],[6068,6069],[6071,6077],[6086,6086],[6089,6099],[6109,6109],[6155,6157],[6313,6313],[6432,6434],[6439,6440],[6450,6450],[6457,6459],[6679,6680],[6912,6915],[6964,6964],[6966,6970],[6972,6972],[6978,6978],[7019,7027],[7616,7626],[7678,7679],[8203,8207],[8234,8238],[8288,8291],[8298,8303],[8400,8431],[12330,12335],[12441,12442],[43014,43014],[43019,43019],[43045,43046],[64286,64286],[65024,65039],[65056,65059],[65279,65279],[65529,65531]],s=[[68097,68099],[68101,68102],[68108,68111],[68152,68154],[68159,68159],[119143,119145],[119155,119170],[119173,119179],[119210,119213],[119362,119364],[917505,917505],[917536,917631],[917760,917999]];let r;t.UnicodeV6=class{constructor(){if(this.version="6",!r){r=new Uint8Array(65536),r.fill(1),r[0]=0,r.fill(0,1,32),r.fill(0,127,160),r.fill(2,4352,4448),r[9001]=2,r[9002]=2,r.fill(2,11904,42192),r[12351]=1,r.fill(2,44032,55204),r.fill(2,63744,64256),r.fill(2,65040,65050),r.fill(2,65072,65136),r.fill(2,65280,65377),r.fill(2,65504,65511);for(let e=0;et[r][1])return!1;for(;r>=s;)if(i=s+r>>1,e>t[i][1])s=i+1;else{if(!(e=131072&&e<=196605||e>=196608&&e<=262141?2:1}}},5981:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.WriteBuffer=void 0;const s=i(8460),r=i(844);class n extends r.Disposable{constructor(e){super(),this._action=e,this._writeBuffer=[],this._callbacks=[],this._pendingData=0,this._bufferOffset=0,this._isSyncWriting=!1,this._syncCalls=0,this._didUserInput=!1,this._onWriteParsed=this.register(new s.EventEmitter),this.onWriteParsed=this._onWriteParsed.event}handleUserInput(){this._didUserInput=!0}writeSync(e,t){if(void 0!==t&&this._syncCalls>t)return void(this._syncCalls=0);if(this._pendingData+=e.length,this._writeBuffer.push(e),this._callbacks.push(void 0),this._syncCalls++,this._isSyncWriting)return;let i;for(this._isSyncWriting=!0;i=this._writeBuffer.shift();){this._action(i);const e=this._callbacks.shift();e&&e()}this._pendingData=0,this._bufferOffset=2147483647,this._isSyncWriting=!1,this._syncCalls=0}write(e,t){if(this._pendingData>5e7)throw new Error("write data discarded, use flow control to avoid losing data");if(!this._writeBuffer.length){if(this._bufferOffset=0,this._didUserInput)return this._didUserInput=!1,this._pendingData+=e.length,this._writeBuffer.push(e),this._callbacks.push(t),void this._innerWrite();setTimeout((()=>this._innerWrite()))}this._pendingData+=e.length,this._writeBuffer.push(e),this._callbacks.push(t)}_innerWrite(e=0,t=!0){const i=e||Date.now();for(;this._writeBuffer.length>this._bufferOffset;){const e=this._writeBuffer[this._bufferOffset],s=this._action(e,t);if(s){const e=e=>Date.now()-i>=12?setTimeout((()=>this._innerWrite(0,e))):this._innerWrite(i,e);return void s.catch((e=>(queueMicrotask((()=>{throw e})),Promise.resolve(!1)))).then(e)}const r=this._callbacks[this._bufferOffset];if(r&&r(),this._bufferOffset++,this._pendingData-=e.length,Date.now()-i>=12)break}this._writeBuffer.length>this._bufferOffset?(this._bufferOffset>50&&(this._writeBuffer=this._writeBuffer.slice(this._bufferOffset),this._callbacks=this._callbacks.slice(this._bufferOffset),this._bufferOffset=0),setTimeout((()=>this._innerWrite()))):(this._writeBuffer.length=0,this._callbacks.length=0,this._pendingData=0,this._bufferOffset=0),this._onWriteParsed.fire()}}t.WriteBuffer=n},5941:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.toRgbString=t.parseColor=void 0;const i=/^([\da-f])\/([\da-f])\/([\da-f])$|^([\da-f]{2})\/([\da-f]{2})\/([\da-f]{2})$|^([\da-f]{3})\/([\da-f]{3})\/([\da-f]{3})$|^([\da-f]{4})\/([\da-f]{4})\/([\da-f]{4})$/,s=/^[\da-f]+$/;function r(e,t){const i=e.toString(16),s=i.length<2?"0"+i:i;switch(t){case 4:return i[0];case 8:return s;case 12:return(s+s).slice(0,3);default:return s+s}}t.parseColor=function(e){if(!e)return;let t=e.toLowerCase();if(0===t.indexOf("rgb:")){t=t.slice(4);const e=i.exec(t);if(e){const t=e[1]?15:e[4]?255:e[7]?4095:65535;return[Math.round(parseInt(e[1]||e[4]||e[7]||e[10],16)/t*255),Math.round(parseInt(e[2]||e[5]||e[8]||e[11],16)/t*255),Math.round(parseInt(e[3]||e[6]||e[9]||e[12],16)/t*255)]}}else if(0===t.indexOf("#")&&(t=t.slice(1),s.exec(t)&&[3,6,9,12].includes(t.length))){const e=t.length/3,i=[0,0,0];for(let s=0;s<3;++s){const r=parseInt(t.slice(e*s,e*s+e),16);i[s]=1===e?r<<4:2===e?r:3===e?r>>4:r>>8}return i}},t.toRgbString=function(e,t=16){const[i,s,n]=e;return`rgb:${r(i,t)}/${r(s,t)}/${r(n,t)}`}},5770:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.PAYLOAD_LIMIT=void 0,t.PAYLOAD_LIMIT=1e7},6351:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.DcsHandler=t.DcsParser=void 0;const s=i(482),r=i(8742),n=i(5770),o=[];t.DcsParser=class{constructor(){this._handlers=Object.create(null),this._active=o,this._ident=0,this._handlerFb=()=>{},this._stack={paused:!1,loopPosition:0,fallThrough:!1}}dispose(){this._handlers=Object.create(null),this._handlerFb=()=>{},this._active=o}registerHandler(e,t){void 0===this._handlers[e]&&(this._handlers[e]=[]);const i=this._handlers[e];return i.push(t),{dispose:()=>{const e=i.indexOf(t);-1!==e&&i.splice(e,1)}}}clearHandler(e){this._handlers[e]&&delete this._handlers[e]}setHandlerFallback(e){this._handlerFb=e}reset(){if(this._active.length)for(let e=this._stack.paused?this._stack.loopPosition-1:this._active.length-1;e>=0;--e)this._active[e].unhook(!1);this._stack.paused=!1,this._active=o,this._ident=0}hook(e,t){if(this.reset(),this._ident=e,this._active=this._handlers[e]||o,this._active.length)for(let e=this._active.length-1;e>=0;e--)this._active[e].hook(t);else this._handlerFb(this._ident,"HOOK",t)}put(e,t,i){if(this._active.length)for(let s=this._active.length-1;s>=0;s--)this._active[s].put(e,t,i);else this._handlerFb(this._ident,"PUT",(0,s.utf32ToString)(e,t,i))}unhook(e,t=!0){if(this._active.length){let i=!1,s=this._active.length-1,r=!1;if(this._stack.paused&&(s=this._stack.loopPosition-1,i=t,r=this._stack.fallThrough,this._stack.paused=!1),!r&&!1===i){for(;s>=0&&(i=this._active[s].unhook(e),!0!==i);s--)if(i instanceof Promise)return this._stack.paused=!0,this._stack.loopPosition=s,this._stack.fallThrough=!1,i;s--}for(;s>=0;s--)if(i=this._active[s].unhook(!1),i instanceof Promise)return this._stack.paused=!0,this._stack.loopPosition=s,this._stack.fallThrough=!0,i}else this._handlerFb(this._ident,"UNHOOK",e);this._active=o,this._ident=0}};const a=new r.Params;a.addParam(0),t.DcsHandler=class{constructor(e){this._handler=e,this._data="",this._params=a,this._hitLimit=!1}hook(e){this._params=e.length>1||e.params[0]?e.clone():a,this._data="",this._hitLimit=!1}put(e,t,i){this._hitLimit||(this._data+=(0,s.utf32ToString)(e,t,i),this._data.length>n.PAYLOAD_LIMIT&&(this._data="",this._hitLimit=!0))}unhook(e){let t=!1;if(this._hitLimit)t=!1;else if(e&&(t=this._handler(this._data,this._params),t instanceof Promise))return t.then((e=>(this._params=a,this._data="",this._hitLimit=!1,e)));return this._params=a,this._data="",this._hitLimit=!1,t}}},2015:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.EscapeSequenceParser=t.VT500_TRANSITION_TABLE=t.TransitionTable=void 0;const s=i(844),r=i(8742),n=i(6242),o=i(6351);class a{constructor(e){this.table=new Uint8Array(e)}setDefault(e,t){this.table.fill(e<<4|t)}add(e,t,i,s){this.table[t<<8|e]=i<<4|s}addMany(e,t,i,s){for(let r=0;rt)),i=(e,i)=>t.slice(e,i),s=i(32,127),r=i(0,24);r.push(25),r.push.apply(r,i(28,32));const n=i(0,14);let o;for(o in e.setDefault(1,0),e.addMany(s,0,2,0),n)e.addMany([24,26,153,154],o,3,0),e.addMany(i(128,144),o,3,0),e.addMany(i(144,152),o,3,0),e.add(156,o,0,0),e.add(27,o,11,1),e.add(157,o,4,8),e.addMany([152,158,159],o,0,7),e.add(155,o,11,3),e.add(144,o,11,9);return e.addMany(r,0,3,0),e.addMany(r,1,3,1),e.add(127,1,0,1),e.addMany(r,8,0,8),e.addMany(r,3,3,3),e.add(127,3,0,3),e.addMany(r,4,3,4),e.add(127,4,0,4),e.addMany(r,6,3,6),e.addMany(r,5,3,5),e.add(127,5,0,5),e.addMany(r,2,3,2),e.add(127,2,0,2),e.add(93,1,4,8),e.addMany(s,8,5,8),e.add(127,8,5,8),e.addMany([156,27,24,26,7],8,6,0),e.addMany(i(28,32),8,0,8),e.addMany([88,94,95],1,0,7),e.addMany(s,7,0,7),e.addMany(r,7,0,7),e.add(156,7,0,0),e.add(127,7,0,7),e.add(91,1,11,3),e.addMany(i(64,127),3,7,0),e.addMany(i(48,60),3,8,4),e.addMany([60,61,62,63],3,9,4),e.addMany(i(48,60),4,8,4),e.addMany(i(64,127),4,7,0),e.addMany([60,61,62,63],4,0,6),e.addMany(i(32,64),6,0,6),e.add(127,6,0,6),e.addMany(i(64,127),6,0,0),e.addMany(i(32,48),3,9,5),e.addMany(i(32,48),5,9,5),e.addMany(i(48,64),5,0,6),e.addMany(i(64,127),5,7,0),e.addMany(i(32,48),4,9,5),e.addMany(i(32,48),1,9,2),e.addMany(i(32,48),2,9,2),e.addMany(i(48,127),2,10,0),e.addMany(i(48,80),1,10,0),e.addMany(i(81,88),1,10,0),e.addMany([89,90,92],1,10,0),e.addMany(i(96,127),1,10,0),e.add(80,1,11,9),e.addMany(r,9,0,9),e.add(127,9,0,9),e.addMany(i(28,32),9,0,9),e.addMany(i(32,48),9,9,12),e.addMany(i(48,60),9,8,10),e.addMany([60,61,62,63],9,9,10),e.addMany(r,11,0,11),e.addMany(i(32,128),11,0,11),e.addMany(i(28,32),11,0,11),e.addMany(r,10,0,10),e.add(127,10,0,10),e.addMany(i(28,32),10,0,10),e.addMany(i(48,60),10,8,10),e.addMany([60,61,62,63],10,0,11),e.addMany(i(32,48),10,9,12),e.addMany(r,12,0,12),e.add(127,12,0,12),e.addMany(i(28,32),12,0,12),e.addMany(i(32,48),12,9,12),e.addMany(i(48,64),12,0,11),e.addMany(i(64,127),12,12,13),e.addMany(i(64,127),10,12,13),e.addMany(i(64,127),9,12,13),e.addMany(r,13,13,13),e.addMany(s,13,13,13),e.add(127,13,0,13),e.addMany([27,156,24,26],13,14,0),e.add(h,0,2,0),e.add(h,8,5,8),e.add(h,6,0,6),e.add(h,11,0,11),e.add(h,13,13,13),e}();class c extends s.Disposable{constructor(e=t.VT500_TRANSITION_TABLE){super(),this._transitions=e,this._parseStack={state:0,handlers:[],handlerPos:0,transition:0,chunkPos:0},this.initialState=0,this.currentState=this.initialState,this._params=new r.Params,this._params.addParam(0),this._collect=0,this.precedingCodepoint=0,this._printHandlerFb=(e,t,i)=>{},this._executeHandlerFb=e=>{},this._csiHandlerFb=(e,t)=>{},this._escHandlerFb=e=>{},this._errorHandlerFb=e=>e,this._printHandler=this._printHandlerFb,this._executeHandlers=Object.create(null),this._csiHandlers=Object.create(null),this._escHandlers=Object.create(null),this.register((0,s.toDisposable)((()=>{this._csiHandlers=Object.create(null),this._executeHandlers=Object.create(null),this._escHandlers=Object.create(null)}))),this._oscParser=this.register(new n.OscParser),this._dcsParser=this.register(new o.DcsParser),this._errorHandler=this._errorHandlerFb,this.registerEscHandler({final:"\\"},(()=>!0))}_identifier(e,t=[64,126]){let i=0;if(e.prefix){if(e.prefix.length>1)throw new Error("only one byte as prefix supported");if(i=e.prefix.charCodeAt(0),i&&60>i||i>63)throw new Error("prefix must be in range 0x3c .. 0x3f")}if(e.intermediates){if(e.intermediates.length>2)throw new Error("only two bytes as intermediates are supported");for(let t=0;ts||s>47)throw new Error("intermediate must be in range 0x20 .. 0x2f");i<<=8,i|=s}}if(1!==e.final.length)throw new Error("final must be a single byte");const s=e.final.charCodeAt(0);if(t[0]>s||s>t[1])throw new Error(`final must be in range ${t[0]} .. ${t[1]}`);return i<<=8,i|=s,i}identToString(e){const t=[];for(;e;)t.push(String.fromCharCode(255&e)),e>>=8;return t.reverse().join("")}setPrintHandler(e){this._printHandler=e}clearPrintHandler(){this._printHandler=this._printHandlerFb}registerEscHandler(e,t){const i=this._identifier(e,[48,126]);void 0===this._escHandlers[i]&&(this._escHandlers[i]=[]);const s=this._escHandlers[i];return s.push(t),{dispose:()=>{const e=s.indexOf(t);-1!==e&&s.splice(e,1)}}}clearEscHandler(e){this._escHandlers[this._identifier(e,[48,126])]&&delete this._escHandlers[this._identifier(e,[48,126])]}setEscHandlerFallback(e){this._escHandlerFb=e}setExecuteHandler(e,t){this._executeHandlers[e.charCodeAt(0)]=t}clearExecuteHandler(e){this._executeHandlers[e.charCodeAt(0)]&&delete this._executeHandlers[e.charCodeAt(0)]}setExecuteHandlerFallback(e){this._executeHandlerFb=e}registerCsiHandler(e,t){const i=this._identifier(e);void 0===this._csiHandlers[i]&&(this._csiHandlers[i]=[]);const s=this._csiHandlers[i];return s.push(t),{dispose:()=>{const e=s.indexOf(t);-1!==e&&s.splice(e,1)}}}clearCsiHandler(e){this._csiHandlers[this._identifier(e)]&&delete this._csiHandlers[this._identifier(e)]}setCsiHandlerFallback(e){this._csiHandlerFb=e}registerDcsHandler(e,t){return this._dcsParser.registerHandler(this._identifier(e),t)}clearDcsHandler(e){this._dcsParser.clearHandler(this._identifier(e))}setDcsHandlerFallback(e){this._dcsParser.setHandlerFallback(e)}registerOscHandler(e,t){return this._oscParser.registerHandler(e,t)}clearOscHandler(e){this._oscParser.clearHandler(e)}setOscHandlerFallback(e){this._oscParser.setHandlerFallback(e)}setErrorHandler(e){this._errorHandler=e}clearErrorHandler(){this._errorHandler=this._errorHandlerFb}reset(){this.currentState=this.initialState,this._oscParser.reset(),this._dcsParser.reset(),this._params.reset(),this._params.addParam(0),this._collect=0,this.precedingCodepoint=0,0!==this._parseStack.state&&(this._parseStack.state=2,this._parseStack.handlers=[])}_preserveStack(e,t,i,s,r){this._parseStack.state=e,this._parseStack.handlers=t,this._parseStack.handlerPos=i,this._parseStack.transition=s,this._parseStack.chunkPos=r}parse(e,t,i){let s,r=0,n=0,o=0;if(this._parseStack.state)if(2===this._parseStack.state)this._parseStack.state=0,o=this._parseStack.chunkPos+1;else{if(void 0===i||1===this._parseStack.state)throw this._parseStack.state=1,new Error("improper continuation due to previous async handler, giving up parsing");const t=this._parseStack.handlers;let n=this._parseStack.handlerPos-1;switch(this._parseStack.state){case 3:if(!1===i&&n>-1)for(;n>=0&&(s=t[n](this._params),!0!==s);n--)if(s instanceof Promise)return this._parseStack.handlerPos=n,s;this._parseStack.handlers=[];break;case 4:if(!1===i&&n>-1)for(;n>=0&&(s=t[n](),!0!==s);n--)if(s instanceof Promise)return this._parseStack.handlerPos=n,s;this._parseStack.handlers=[];break;case 6:if(r=e[this._parseStack.chunkPos],s=this._dcsParser.unhook(24!==r&&26!==r,i),s)return s;27===r&&(this._parseStack.transition|=1),this._params.reset(),this._params.addParam(0),this._collect=0;break;case 5:if(r=e[this._parseStack.chunkPos],s=this._oscParser.end(24!==r&&26!==r,i),s)return s;27===r&&(this._parseStack.transition|=1),this._params.reset(),this._params.addParam(0),this._collect=0}this._parseStack.state=0,o=this._parseStack.chunkPos+1,this.precedingCodepoint=0,this.currentState=15&this._parseStack.transition}for(let i=o;i>4){case 2:for(let s=i+1;;++s){if(s>=t||(r=e[s])<32||r>126&&r=t||(r=e[s])<32||r>126&&r=t||(r=e[s])<32||r>126&&r=t||(r=e[s])<32||r>126&&r=0&&(s=o[a](this._params),!0!==s);a--)if(s instanceof Promise)return this._preserveStack(3,o,a,n,i),s;a<0&&this._csiHandlerFb(this._collect<<8|r,this._params),this.precedingCodepoint=0;break;case 8:do{switch(r){case 59:this._params.addParam(0);break;case 58:this._params.addSubParam(-1);break;default:this._params.addDigit(r-48)}}while(++i47&&r<60);i--;break;case 9:this._collect<<=8,this._collect|=r;break;case 10:const c=this._escHandlers[this._collect<<8|r];let l=c?c.length-1:-1;for(;l>=0&&(s=c[l](),!0!==s);l--)if(s instanceof Promise)return this._preserveStack(4,c,l,n,i),s;l<0&&this._escHandlerFb(this._collect<<8|r),this.precedingCodepoint=0;break;case 11:this._params.reset(),this._params.addParam(0),this._collect=0;break;case 12:this._dcsParser.hook(this._collect<<8|r,this._params);break;case 13:for(let s=i+1;;++s)if(s>=t||24===(r=e[s])||26===r||27===r||r>127&&r=t||(r=e[s])<32||r>127&&r{Object.defineProperty(t,"__esModule",{value:!0}),t.OscHandler=t.OscParser=void 0;const s=i(5770),r=i(482),n=[];t.OscParser=class{constructor(){this._state=0,this._active=n,this._id=-1,this._handlers=Object.create(null),this._handlerFb=()=>{},this._stack={paused:!1,loopPosition:0,fallThrough:!1}}registerHandler(e,t){void 0===this._handlers[e]&&(this._handlers[e]=[]);const i=this._handlers[e];return i.push(t),{dispose:()=>{const e=i.indexOf(t);-1!==e&&i.splice(e,1)}}}clearHandler(e){this._handlers[e]&&delete this._handlers[e]}setHandlerFallback(e){this._handlerFb=e}dispose(){this._handlers=Object.create(null),this._handlerFb=()=>{},this._active=n}reset(){if(2===this._state)for(let e=this._stack.paused?this._stack.loopPosition-1:this._active.length-1;e>=0;--e)this._active[e].end(!1);this._stack.paused=!1,this._active=n,this._id=-1,this._state=0}_start(){if(this._active=this._handlers[this._id]||n,this._active.length)for(let e=this._active.length-1;e>=0;e--)this._active[e].start();else this._handlerFb(this._id,"START")}_put(e,t,i){if(this._active.length)for(let s=this._active.length-1;s>=0;s--)this._active[s].put(e,t,i);else this._handlerFb(this._id,"PUT",(0,r.utf32ToString)(e,t,i))}start(){this.reset(),this._state=1}put(e,t,i){if(3!==this._state){if(1===this._state)for(;t0&&this._put(e,t,i)}}end(e,t=!0){if(0!==this._state){if(3!==this._state)if(1===this._state&&this._start(),this._active.length){let i=!1,s=this._active.length-1,r=!1;if(this._stack.paused&&(s=this._stack.loopPosition-1,i=t,r=this._stack.fallThrough,this._stack.paused=!1),!r&&!1===i){for(;s>=0&&(i=this._active[s].end(e),!0!==i);s--)if(i instanceof Promise)return this._stack.paused=!0,this._stack.loopPosition=s,this._stack.fallThrough=!1,i;s--}for(;s>=0;s--)if(i=this._active[s].end(!1),i instanceof Promise)return this._stack.paused=!0,this._stack.loopPosition=s,this._stack.fallThrough=!0,i}else this._handlerFb(this._id,"END",e);this._active=n,this._id=-1,this._state=0}}},t.OscHandler=class{constructor(e){this._handler=e,this._data="",this._hitLimit=!1}start(){this._data="",this._hitLimit=!1}put(e,t,i){this._hitLimit||(this._data+=(0,r.utf32ToString)(e,t,i),this._data.length>s.PAYLOAD_LIMIT&&(this._data="",this._hitLimit=!0))}end(e){let t=!1;if(this._hitLimit)t=!1;else if(e&&(t=this._handler(this._data),t instanceof Promise))return t.then((e=>(this._data="",this._hitLimit=!1,e)));return this._data="",this._hitLimit=!1,t}}},8742:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.Params=void 0;const i=2147483647;class s{static fromArray(e){const t=new s;if(!e.length)return t;for(let i=Array.isArray(e[0])?1:0;i256)throw new Error("maxSubParamsLength must not be greater than 256");this.params=new Int32Array(e),this.length=0,this._subParams=new Int32Array(t),this._subParamsLength=0,this._subParamsIdx=new Uint16Array(e),this._rejectDigits=!1,this._rejectSubDigits=!1,this._digitIsSub=!1}clone(){const e=new s(this.maxLength,this.maxSubParamsLength);return e.params.set(this.params),e.length=this.length,e._subParams.set(this._subParams),e._subParamsLength=this._subParamsLength,e._subParamsIdx.set(this._subParamsIdx),e._rejectDigits=this._rejectDigits,e._rejectSubDigits=this._rejectSubDigits,e._digitIsSub=this._digitIsSub,e}toArray(){const e=[];for(let t=0;t>8,s=255&this._subParamsIdx[t];s-i>0&&e.push(Array.prototype.slice.call(this._subParams,i,s))}return e}reset(){this.length=0,this._subParamsLength=0,this._rejectDigits=!1,this._rejectSubDigits=!1,this._digitIsSub=!1}addParam(e){if(this._digitIsSub=!1,this.length>=this.maxLength)this._rejectDigits=!0;else{if(e<-1)throw new Error("values lesser than -1 are not allowed");this._subParamsIdx[this.length]=this._subParamsLength<<8|this._subParamsLength,this.params[this.length++]=e>i?i:e}}addSubParam(e){if(this._digitIsSub=!0,this.length)if(this._rejectDigits||this._subParamsLength>=this.maxSubParamsLength)this._rejectSubDigits=!0;else{if(e<-1)throw new Error("values lesser than -1 are not allowed");this._subParams[this._subParamsLength++]=e>i?i:e,this._subParamsIdx[this.length-1]++}}hasSubParams(e){return(255&this._subParamsIdx[e])-(this._subParamsIdx[e]>>8)>0}getSubParams(e){const t=this._subParamsIdx[e]>>8,i=255&this._subParamsIdx[e];return i-t>0?this._subParams.subarray(t,i):null}getSubParamsAll(){const e={};for(let t=0;t>8,s=255&this._subParamsIdx[t];s-i>0&&(e[t]=this._subParams.slice(i,s))}return e}addDigit(e){let t;if(this._rejectDigits||!(t=this._digitIsSub?this._subParamsLength:this.length)||this._digitIsSub&&this._rejectSubDigits)return;const s=this._digitIsSub?this._subParams:this.params,r=s[t-1];s[t-1]=~r?Math.min(10*r+e,i):e}}t.Params=s},5741:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.AddonManager=void 0,t.AddonManager=class{constructor(){this._addons=[]}dispose(){for(let e=this._addons.length-1;e>=0;e--)this._addons[e].instance.dispose()}loadAddon(e,t){const i={instance:t,dispose:t.dispose,isDisposed:!1};this._addons.push(i),t.dispose=()=>this._wrappedAddonDispose(i),t.activate(e)}_wrappedAddonDispose(e){if(e.isDisposed)return;let t=-1;for(let i=0;i{Object.defineProperty(t,"__esModule",{value:!0}),t.BufferApiView=void 0;const s=i(3785),r=i(511);t.BufferApiView=class{constructor(e,t){this._buffer=e,this.type=t}init(e){return this._buffer=e,this}get cursorY(){return this._buffer.y}get cursorX(){return this._buffer.x}get viewportY(){return this._buffer.ydisp}get baseY(){return this._buffer.ybase}get length(){return this._buffer.lines.length}getLine(e){const t=this._buffer.lines.get(e);if(t)return new s.BufferLineApiView(t)}getNullCell(){return new r.CellData}}},3785:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.BufferLineApiView=void 0;const s=i(511);t.BufferLineApiView=class{constructor(e){this._line=e}get isWrapped(){return this._line.isWrapped}get length(){return this._line.length}getCell(e,t){if(!(e<0||e>=this._line.length))return t?(this._line.loadCell(e,t),t):this._line.loadCell(e,new s.CellData)}translateToString(e,t,i){return this._line.translateToString(e,t,i)}}},8285:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.BufferNamespaceApi=void 0;const s=i(8771),r=i(8460),n=i(844);class o extends n.Disposable{constructor(e){super(),this._core=e,this._onBufferChange=this.register(new r.EventEmitter),this.onBufferChange=this._onBufferChange.event,this._normal=new s.BufferApiView(this._core.buffers.normal,"normal"),this._alternate=new s.BufferApiView(this._core.buffers.alt,"alternate"),this._core.buffers.onBufferActivate((()=>this._onBufferChange.fire(this.active)))}get active(){if(this._core.buffers.active===this._core.buffers.normal)return this.normal;if(this._core.buffers.active===this._core.buffers.alt)return this.alternate;throw new Error("Active buffer is neither normal nor alternate")}get normal(){return this._normal.init(this._core.buffers.normal)}get alternate(){return this._alternate.init(this._core.buffers.alt)}}t.BufferNamespaceApi=o},7975:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.ParserApi=void 0,t.ParserApi=class{constructor(e){this._core=e}registerCsiHandler(e,t){return this._core.registerCsiHandler(e,(e=>t(e.toArray())))}addCsiHandler(e,t){return this.registerCsiHandler(e,t)}registerDcsHandler(e,t){return this._core.registerDcsHandler(e,((e,i)=>t(e,i.toArray())))}addDcsHandler(e,t){return this.registerDcsHandler(e,t)}registerEscHandler(e,t){return this._core.registerEscHandler(e,t)}addEscHandler(e,t){return this.registerEscHandler(e,t)}registerOscHandler(e,t){return this._core.registerOscHandler(e,t)}addOscHandler(e,t){return this.registerOscHandler(e,t)}}},7090:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.UnicodeApi=void 0,t.UnicodeApi=class{constructor(e){this._core=e}register(e){this._core.unicodeService.register(e)}get versions(){return this._core.unicodeService.versions}get activeVersion(){return this._core.unicodeService.activeVersion}set activeVersion(e){this._core.unicodeService.activeVersion=e}}},744:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.BufferService=t.MINIMUM_ROWS=t.MINIMUM_COLS=void 0;const n=i(8460),o=i(844),a=i(5295),h=i(2585);t.MINIMUM_COLS=2,t.MINIMUM_ROWS=1;let c=t.BufferService=class extends o.Disposable{get buffer(){return this.buffers.active}constructor(e){super(),this.isUserScrolling=!1,this._onResize=this.register(new n.EventEmitter),this.onResize=this._onResize.event,this._onScroll=this.register(new n.EventEmitter),this.onScroll=this._onScroll.event,this.cols=Math.max(e.rawOptions.cols||0,t.MINIMUM_COLS),this.rows=Math.max(e.rawOptions.rows||0,t.MINIMUM_ROWS),this.buffers=this.register(new a.BufferSet(e,this))}resize(e,t){this.cols=e,this.rows=t,this.buffers.resize(e,t),this._onResize.fire({cols:e,rows:t})}reset(){this.buffers.reset(),this.isUserScrolling=!1}scroll(e,t=!1){const i=this.buffer;let s;s=this._cachedBlankLine,s&&s.length===this.cols&&s.getFg(0)===e.fg&&s.getBg(0)===e.bg||(s=i.getBlankLine(e,t),this._cachedBlankLine=s),s.isWrapped=t;const r=i.ybase+i.scrollTop,n=i.ybase+i.scrollBottom;if(0===i.scrollTop){const e=i.lines.isFull;n===i.lines.length-1?e?i.lines.recycle().copyFrom(s):i.lines.push(s.clone()):i.lines.splice(n+1,0,s.clone()),e?this.isUserScrolling&&(i.ydisp=Math.max(i.ydisp-1,0)):(i.ybase++,this.isUserScrolling||i.ydisp++)}else{const e=n-r+1;i.lines.shiftElements(r+1,e-1,-1),i.lines.set(n,s.clone())}this.isUserScrolling||(i.ydisp=i.ybase),this._onScroll.fire(i.ydisp)}scrollLines(e,t,i){const s=this.buffer;if(e<0){if(0===s.ydisp)return;this.isUserScrolling=!0}else e+s.ydisp>=s.ybase&&(this.isUserScrolling=!1);const r=s.ydisp;s.ydisp=Math.max(Math.min(s.ydisp+e,s.ybase),0),r!==s.ydisp&&(t||this._onScroll.fire(s.ydisp))}};t.BufferService=c=s([r(0,h.IOptionsService)],c)},7994:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.CharsetService=void 0,t.CharsetService=class{constructor(){this.glevel=0,this._charsets=[]}reset(){this.charset=void 0,this._charsets=[],this.glevel=0}setgLevel(e){this.glevel=e,this.charset=this._charsets[e]}setgCharset(e,t){this._charsets[e]=t,this.glevel===e&&(this.charset=t)}}},1753:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.CoreMouseService=void 0;const n=i(2585),o=i(8460),a=i(844),h={NONE:{events:0,restrict:()=>!1},X10:{events:1,restrict:e=>4!==e.button&&1===e.action&&(e.ctrl=!1,e.alt=!1,e.shift=!1,!0)},VT200:{events:19,restrict:e=>32!==e.action},DRAG:{events:23,restrict:e=>32!==e.action||3!==e.button},ANY:{events:31,restrict:e=>!0}};function c(e,t){let i=(e.ctrl?16:0)|(e.shift?4:0)|(e.alt?8:0);return 4===e.button?(i|=64,i|=e.action):(i|=3&e.button,4&e.button&&(i|=64),8&e.button&&(i|=128),32===e.action?i|=32:0!==e.action||t||(i|=3)),i}const l=String.fromCharCode,d={DEFAULT:e=>{const t=[c(e,!1)+32,e.col+32,e.row+32];return t[0]>255||t[1]>255||t[2]>255?"":`${l(t[0])}${l(t[1])}${l(t[2])}`},SGR:e=>{const t=0===e.action&&4!==e.button?"m":"M";return`[<${c(e,!0)};${e.col};${e.row}${t}`},SGR_PIXELS:e=>{const t=0===e.action&&4!==e.button?"m":"M";return`[<${c(e,!0)};${e.x};${e.y}${t}`}};let _=t.CoreMouseService=class extends a.Disposable{constructor(e,t){super(),this._bufferService=e,this._coreService=t,this._protocols={},this._encodings={},this._activeProtocol="",this._activeEncoding="",this._lastEvent=null,this._onProtocolChange=this.register(new o.EventEmitter),this.onProtocolChange=this._onProtocolChange.event;for(const e of Object.keys(h))this.addProtocol(e,h[e]);for(const e of Object.keys(d))this.addEncoding(e,d[e]);this.reset()}addProtocol(e,t){this._protocols[e]=t}addEncoding(e,t){this._encodings[e]=t}get activeProtocol(){return this._activeProtocol}get areMouseEventsActive(){return 0!==this._protocols[this._activeProtocol].events}set activeProtocol(e){if(!this._protocols[e])throw new Error(`unknown protocol "${e}"`);this._activeProtocol=e,this._onProtocolChange.fire(this._protocols[e].events)}get activeEncoding(){return this._activeEncoding}set activeEncoding(e){if(!this._encodings[e])throw new Error(`unknown encoding "${e}"`);this._activeEncoding=e}reset(){this.activeProtocol="NONE",this.activeEncoding="DEFAULT",this._lastEvent=null}triggerMouseEvent(e){if(e.col<0||e.col>=this._bufferService.cols||e.row<0||e.row>=this._bufferService.rows)return!1;if(4===e.button&&32===e.action)return!1;if(3===e.button&&32!==e.action)return!1;if(4!==e.button&&(2===e.action||3===e.action))return!1;if(e.col++,e.row++,32===e.action&&this._lastEvent&&this._equalEvents(this._lastEvent,e,"SGR_PIXELS"===this._activeEncoding))return!1;if(!this._protocols[this._activeProtocol].restrict(e))return!1;const t=this._encodings[this._activeEncoding](e);return t&&("DEFAULT"===this._activeEncoding?this._coreService.triggerBinaryEvent(t):this._coreService.triggerDataEvent(t,!0)),this._lastEvent=e,!0}explainEvents(e){return{down:!!(1&e),up:!!(2&e),drag:!!(4&e),move:!!(8&e),wheel:!!(16&e)}}_equalEvents(e,t,i){if(i){if(e.x!==t.x)return!1;if(e.y!==t.y)return!1}else{if(e.col!==t.col)return!1;if(e.row!==t.row)return!1}return e.button===t.button&&e.action===t.action&&e.ctrl===t.ctrl&&e.alt===t.alt&&e.shift===t.shift}};t.CoreMouseService=_=s([r(0,n.IBufferService),r(1,n.ICoreService)],_)},6975:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.CoreService=void 0;const n=i(1439),o=i(8460),a=i(844),h=i(2585),c=Object.freeze({insertMode:!1}),l=Object.freeze({applicationCursorKeys:!1,applicationKeypad:!1,bracketedPasteMode:!1,origin:!1,reverseWraparound:!1,sendFocus:!1,wraparound:!0});let d=t.CoreService=class extends a.Disposable{constructor(e,t,i){super(),this._bufferService=e,this._logService=t,this._optionsService=i,this.isCursorInitialized=!1,this.isCursorHidden=!1,this._onData=this.register(new o.EventEmitter),this.onData=this._onData.event,this._onUserInput=this.register(new o.EventEmitter),this.onUserInput=this._onUserInput.event,this._onBinary=this.register(new o.EventEmitter),this.onBinary=this._onBinary.event,this._onRequestScrollToBottom=this.register(new o.EventEmitter),this.onRequestScrollToBottom=this._onRequestScrollToBottom.event,this.modes=(0,n.clone)(c),this.decPrivateModes=(0,n.clone)(l)}reset(){this.modes=(0,n.clone)(c),this.decPrivateModes=(0,n.clone)(l)}triggerDataEvent(e,t=!1){if(this._optionsService.rawOptions.disableStdin)return;const i=this._bufferService.buffer;t&&this._optionsService.rawOptions.scrollOnUserInput&&i.ybase!==i.ydisp&&this._onRequestScrollToBottom.fire(),t&&this._onUserInput.fire(),this._logService.debug(`sending data "${e}"`,(()=>e.split("").map((e=>e.charCodeAt(0))))),this._onData.fire(e)}triggerBinaryEvent(e){this._optionsService.rawOptions.disableStdin||(this._logService.debug(`sending binary "${e}"`,(()=>e.split("").map((e=>e.charCodeAt(0))))),this._onBinary.fire(e))}};t.CoreService=d=s([r(0,h.IBufferService),r(1,h.ILogService),r(2,h.IOptionsService)],d)},9074:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.DecorationService=void 0;const s=i(8055),r=i(8460),n=i(844),o=i(6106);let a=0,h=0;class c extends n.Disposable{get decorations(){return this._decorations.values()}constructor(){super(),this._decorations=new o.SortedList((e=>null==e?void 0:e.marker.line)),this._onDecorationRegistered=this.register(new r.EventEmitter),this.onDecorationRegistered=this._onDecorationRegistered.event,this._onDecorationRemoved=this.register(new r.EventEmitter),this.onDecorationRemoved=this._onDecorationRemoved.event,this.register((0,n.toDisposable)((()=>this.reset())))}registerDecoration(e){if(e.marker.isDisposed)return;const t=new l(e);if(t){const e=t.marker.onDispose((()=>t.dispose()));t.onDispose((()=>{t&&(this._decorations.delete(t)&&this._onDecorationRemoved.fire(t),e.dispose())})),this._decorations.insert(t),this._onDecorationRegistered.fire(t)}return t}reset(){for(const e of this._decorations.values())e.dispose();this._decorations.clear()}*getDecorationsAtCell(e,t,i){var s,r,n;let o=0,a=0;for(const h of this._decorations.getKeyIterator(t))o=null!==(s=h.options.x)&&void 0!==s?s:0,a=o+(null!==(r=h.options.width)&&void 0!==r?r:1),e>=o&&e{var r,n,o;a=null!==(r=t.options.x)&&void 0!==r?r:0,h=a+(null!==(n=t.options.width)&&void 0!==n?n:1),e>=a&&e{Object.defineProperty(t,"__esModule",{value:!0}),t.InstantiationService=t.ServiceCollection=void 0;const s=i(2585),r=i(8343);class n{constructor(...e){this._entries=new Map;for(const[t,i]of e)this.set(t,i)}set(e,t){const i=this._entries.get(e);return this._entries.set(e,t),i}forEach(e){for(const[t,i]of this._entries.entries())e(t,i)}has(e){return this._entries.has(e)}get(e){return this._entries.get(e)}}t.ServiceCollection=n,t.InstantiationService=class{constructor(){this._services=new n,this._services.set(s.IInstantiationService,this)}setService(e,t){this._services.set(e,t)}getService(e){return this._services.get(e)}createInstance(e,...t){const i=(0,r.getServiceDependencies)(e).sort(((e,t)=>e.index-t.index)),s=[];for(const t of i){const i=this._services.get(t.id);if(!i)throw new Error(`[createInstance] ${e.name} depends on UNKNOWN service ${t.id}.`);s.push(i)}const n=i.length>0?i[0].index:t.length;if(t.length!==n)throw new Error(`[createInstance] First service dependency of ${e.name} at position ${n+1} conflicts with ${t.length} static arguments`);return new e(...[...t,...s])}}},7866:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.traceCall=t.setTraceLogger=t.LogService=void 0;const n=i(844),o=i(2585),a={trace:o.LogLevelEnum.TRACE,debug:o.LogLevelEnum.DEBUG,info:o.LogLevelEnum.INFO,warn:o.LogLevelEnum.WARN,error:o.LogLevelEnum.ERROR,off:o.LogLevelEnum.OFF};let h,c=t.LogService=class extends n.Disposable{get logLevel(){return this._logLevel}constructor(e){super(),this._optionsService=e,this._logLevel=o.LogLevelEnum.OFF,this._updateLogLevel(),this.register(this._optionsService.onSpecificOptionChange("logLevel",(()=>this._updateLogLevel()))),h=this}_updateLogLevel(){this._logLevel=a[this._optionsService.rawOptions.logLevel]}_evalLazyOptionalParams(e){for(let t=0;tJSON.stringify(e))).join(", ")})`);const t=s.apply(this,e);return h.trace(`GlyphRenderer#${s.name} return`,t),t}}},7302:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.OptionsService=t.DEFAULT_OPTIONS=void 0;const s=i(8460),r=i(844),n=i(6114);t.DEFAULT_OPTIONS={cols:80,rows:24,cursorBlink:!1,cursorStyle:"block",cursorWidth:1,cursorInactiveStyle:"outline",customGlyphs:!0,drawBoldTextInBrightColors:!0,fastScrollModifier:"alt",fastScrollSensitivity:5,fontFamily:"courier-new, courier, monospace",fontSize:15,fontWeight:"normal",fontWeightBold:"bold",ignoreBracketedPasteMode:!1,lineHeight:1,letterSpacing:0,linkHandler:null,logLevel:"info",logger:null,scrollback:1e3,scrollOnUserInput:!0,scrollSensitivity:1,screenReaderMode:!1,smoothScrollDuration:0,macOptionIsMeta:!1,macOptionClickForcesSelection:!1,minimumContrastRatio:1,disableStdin:!1,allowProposedApi:!1,allowTransparency:!1,tabStopWidth:8,theme:{},rightClickSelectsWord:n.isMac,windowOptions:{},windowsMode:!1,windowsPty:{},wordSeparator:" ()[]{}',\"`",altClickMovesCursor:!0,convertEol:!1,termName:"xterm",cancelEvents:!1,overviewRulerWidth:0};const o=["normal","bold","100","200","300","400","500","600","700","800","900"];class a extends r.Disposable{constructor(e){super(),this._onOptionChange=this.register(new s.EventEmitter),this.onOptionChange=this._onOptionChange.event;const i=Object.assign({},t.DEFAULT_OPTIONS);for(const t in e)if(t in i)try{const s=e[t];i[t]=this._sanitizeAndValidateOption(t,s)}catch(e){console.error(e)}this.rawOptions=i,this.options=Object.assign({},i),this._setupOptions()}onSpecificOptionChange(e,t){return this.onOptionChange((i=>{i===e&&t(this.rawOptions[e])}))}onMultipleOptionChange(e,t){return this.onOptionChange((i=>{-1!==e.indexOf(i)&&t()}))}_setupOptions(){const e=e=>{if(!(e in t.DEFAULT_OPTIONS))throw new Error(`No option with key "${e}"`);return this.rawOptions[e]},i=(e,i)=>{if(!(e in t.DEFAULT_OPTIONS))throw new Error(`No option with key "${e}"`);i=this._sanitizeAndValidateOption(e,i),this.rawOptions[e]!==i&&(this.rawOptions[e]=i,this._onOptionChange.fire(e))};for(const t in this.rawOptions){const s={get:e.bind(this,t),set:i.bind(this,t)};Object.defineProperty(this.options,t,s)}}_sanitizeAndValidateOption(e,i){switch(e){case"cursorStyle":if(i||(i=t.DEFAULT_OPTIONS[e]),!function(e){return"block"===e||"underline"===e||"bar"===e}(i))throw new Error(`"${i}" is not a valid value for ${e}`);break;case"wordSeparator":i||(i=t.DEFAULT_OPTIONS[e]);break;case"fontWeight":case"fontWeightBold":if("number"==typeof i&&1<=i&&i<=1e3)break;i=o.includes(i)?i:t.DEFAULT_OPTIONS[e];break;case"cursorWidth":i=Math.floor(i);case"lineHeight":case"tabStopWidth":if(i<1)throw new Error(`${e} cannot be less than 1, value: ${i}`);break;case"minimumContrastRatio":i=Math.max(1,Math.min(21,Math.round(10*i)/10));break;case"scrollback":if((i=Math.min(i,4294967295))<0)throw new Error(`${e} cannot be less than 0, value: ${i}`);break;case"fastScrollSensitivity":case"scrollSensitivity":if(i<=0)throw new Error(`${e} cannot be less than or equal to 0, value: ${i}`);break;case"rows":case"cols":if(!i&&0!==i)throw new Error(`${e} must be numeric, value: ${i}`);break;case"windowsPty":i=null!=i?i:{}}return i}}t.OptionsService=a},2660:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.OscLinkService=void 0;const n=i(2585);let o=t.OscLinkService=class{constructor(e){this._bufferService=e,this._nextId=1,this._entriesWithId=new Map,this._dataByLinkId=new Map}registerLink(e){const t=this._bufferService.buffer;if(void 0===e.id){const i=t.addMarker(t.ybase+t.y),s={data:e,id:this._nextId++,lines:[i]};return i.onDispose((()=>this._removeMarkerFromLink(s,i))),this._dataByLinkId.set(s.id,s),s.id}const i=e,s=this._getEntryIdKey(i),r=this._entriesWithId.get(s);if(r)return this.addLineToLink(r.id,t.ybase+t.y),r.id;const n=t.addMarker(t.ybase+t.y),o={id:this._nextId++,key:this._getEntryIdKey(i),data:i,lines:[n]};return n.onDispose((()=>this._removeMarkerFromLink(o,n))),this._entriesWithId.set(o.key,o),this._dataByLinkId.set(o.id,o),o.id}addLineToLink(e,t){const i=this._dataByLinkId.get(e);if(i&&i.lines.every((e=>e.line!==t))){const e=this._bufferService.buffer.addMarker(t);i.lines.push(e),e.onDispose((()=>this._removeMarkerFromLink(i,e)))}}getLinkData(e){var t;return null===(t=this._dataByLinkId.get(e))||void 0===t?void 0:t.data}_getEntryIdKey(e){return`${e.id};;${e.uri}`}_removeMarkerFromLink(e,t){const i=e.lines.indexOf(t);-1!==i&&(e.lines.splice(i,1),0===e.lines.length&&(void 0!==e.data.id&&this._entriesWithId.delete(e.key),this._dataByLinkId.delete(e.id)))}};t.OscLinkService=o=s([r(0,n.IBufferService)],o)},8343:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.createDecorator=t.getServiceDependencies=t.serviceRegistry=void 0;const i="di$target",s="di$dependencies";t.serviceRegistry=new Map,t.getServiceDependencies=function(e){return e[s]||[]},t.createDecorator=function(e){if(t.serviceRegistry.has(e))return t.serviceRegistry.get(e);const r=function(e,t,n){if(3!==arguments.length)throw new Error("@IServiceName-decorator can only be used to decorate a parameter");!function(e,t,r){t[i]===t?t[s].push({id:e,index:r}):(t[s]=[{id:e,index:r}],t[i]=t)}(r,e,n)};return r.toString=()=>e,t.serviceRegistry.set(e,r),r}},2585:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.IDecorationService=t.IUnicodeService=t.IOscLinkService=t.IOptionsService=t.ILogService=t.LogLevelEnum=t.IInstantiationService=t.ICharsetService=t.ICoreService=t.ICoreMouseService=t.IBufferService=void 0;const s=i(8343);var r;t.IBufferService=(0,s.createDecorator)("BufferService"),t.ICoreMouseService=(0,s.createDecorator)("CoreMouseService"),t.ICoreService=(0,s.createDecorator)("CoreService"),t.ICharsetService=(0,s.createDecorator)("CharsetService"),t.IInstantiationService=(0,s.createDecorator)("InstantiationService"),function(e){e[e.TRACE=0]="TRACE",e[e.DEBUG=1]="DEBUG",e[e.INFO=2]="INFO",e[e.WARN=3]="WARN",e[e.ERROR=4]="ERROR",e[e.OFF=5]="OFF"}(r||(t.LogLevelEnum=r={})),t.ILogService=(0,s.createDecorator)("LogService"),t.IOptionsService=(0,s.createDecorator)("OptionsService"),t.IOscLinkService=(0,s.createDecorator)("OscLinkService"),t.IUnicodeService=(0,s.createDecorator)("UnicodeService"),t.IDecorationService=(0,s.createDecorator)("DecorationService")},1480:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.UnicodeService=void 0;const s=i(8460),r=i(225);t.UnicodeService=class{constructor(){this._providers=Object.create(null),this._active="",this._onChange=new s.EventEmitter,this.onChange=this._onChange.event;const e=new r.UnicodeV6;this.register(e),this._active=e.version,this._activeProvider=e}dispose(){this._onChange.dispose()}get versions(){return Object.keys(this._providers)}get activeVersion(){return this._active}set activeVersion(e){if(!this._providers[e])throw new Error(`unknown Unicode version "${e}"`);this._active=e,this._activeProvider=this._providers[e],this._onChange.fire(e)}register(e){this._providers[e.version]=e}wcwidth(e){return this._activeProvider.wcwidth(e)}getStringCellWidth(e){let t=0;const i=e.length;for(let s=0;s=i)return t+this.wcwidth(r);const n=e.charCodeAt(s);56320<=n&&n<=57343?r=1024*(r-55296)+n-56320+65536:t+=this.wcwidth(n)}t+=this.wcwidth(r)}return t}}}},t={};function i(s){var r=t[s];if(void 0!==r)return r.exports;var n=t[s]={exports:{}};return e[s].call(n.exports,n,n.exports,i),n.exports}var s={};return(()=>{var e=s;Object.defineProperty(e,"__esModule",{value:!0}),e.Terminal=void 0;const t=i(9042),r=i(3236),n=i(844),o=i(5741),a=i(8285),h=i(7975),c=i(7090),l=["cols","rows"];class d extends n.Disposable{constructor(e){super(),this._core=this.register(new r.Terminal(e)),this._addonManager=this.register(new o.AddonManager),this._publicOptions=Object.assign({},this._core.options);const t=e=>this._core.options[e],i=(e,t)=>{this._checkReadonlyOptions(e),this._core.options[e]=t};for(const e in this._core.options){const s={get:t.bind(this,e),set:i.bind(this,e)};Object.defineProperty(this._publicOptions,e,s)}}_checkReadonlyOptions(e){if(l.includes(e))throw new Error(`Option "${e}" can only be set in the constructor`)}_checkProposedApi(){if(!this._core.optionsService.rawOptions.allowProposedApi)throw new Error("You must set the allowProposedApi option to true to use proposed API")}get onBell(){return this._core.onBell}get onBinary(){return this._core.onBinary}get onCursorMove(){return this._core.onCursorMove}get onData(){return this._core.onData}get onKey(){return this._core.onKey}get onLineFeed(){return this._core.onLineFeed}get onRender(){return this._core.onRender}get onResize(){return this._core.onResize}get onScroll(){return this._core.onScroll}get onSelectionChange(){return this._core.onSelectionChange}get onTitleChange(){return this._core.onTitleChange}get onWriteParsed(){return this._core.onWriteParsed}get element(){return this._core.element}get parser(){return this._parser||(this._parser=new h.ParserApi(this._core)),this._parser}get unicode(){return this._checkProposedApi(),new c.UnicodeApi(this._core)}get textarea(){return this._core.textarea}get rows(){return this._core.rows}get cols(){return this._core.cols}get buffer(){return this._buffer||(this._buffer=this.register(new a.BufferNamespaceApi(this._core))),this._buffer}get markers(){return this._checkProposedApi(),this._core.markers}get modes(){const e=this._core.coreService.decPrivateModes;let t="none";switch(this._core.coreMouseService.activeProtocol){case"X10":t="x10";break;case"VT200":t="vt200";break;case"DRAG":t="drag";break;case"ANY":t="any"}return{applicationCursorKeysMode:e.applicationCursorKeys,applicationKeypadMode:e.applicationKeypad,bracketedPasteMode:e.bracketedPasteMode,insertMode:this._core.coreService.modes.insertMode,mouseTrackingMode:t,originMode:e.origin,reverseWraparoundMode:e.reverseWraparound,sendFocusMode:e.sendFocus,wraparoundMode:e.wraparound}}get options(){return this._publicOptions}set options(e){for(const t in e)this._publicOptions[t]=e[t]}blur(){this._core.blur()}focus(){this._core.focus()}resize(e,t){this._verifyIntegers(e,t),this._core.resize(e,t)}open(e){this._core.open(e)}attachCustomKeyEventHandler(e){this._core.attachCustomKeyEventHandler(e)}registerLinkProvider(e){return this._core.registerLinkProvider(e)}registerCharacterJoiner(e){return this._checkProposedApi(),this._core.registerCharacterJoiner(e)}deregisterCharacterJoiner(e){this._checkProposedApi(),this._core.deregisterCharacterJoiner(e)}registerMarker(e=0){return this._verifyIntegers(e),this._core.registerMarker(e)}registerDecoration(e){var t,i,s;return this._checkProposedApi(),this._verifyPositiveIntegers(null!==(t=e.x)&&void 0!==t?t:0,null!==(i=e.width)&&void 0!==i?i:0,null!==(s=e.height)&&void 0!==s?s:0),this._core.registerDecoration(e)}hasSelection(){return this._core.hasSelection()}select(e,t,i){this._verifyIntegers(e,t,i),this._core.select(e,t,i)}getSelection(){return this._core.getSelection()}getSelectionPosition(){return this._core.getSelectionPosition()}clearSelection(){this._core.clearSelection()}selectAll(){this._core.selectAll()}selectLines(e,t){this._verifyIntegers(e,t),this._core.selectLines(e,t)}dispose(){super.dispose()}scrollLines(e){this._verifyIntegers(e),this._core.scrollLines(e)}scrollPages(e){this._verifyIntegers(e),this._core.scrollPages(e)}scrollToTop(){this._core.scrollToTop()}scrollToBottom(){this._core.scrollToBottom()}scrollToLine(e){this._verifyIntegers(e),this._core.scrollToLine(e)}clear(){this._core.clear()}write(e,t){this._core.write(e,t)}writeln(e,t){this._core.write(e),this._core.write("\r\n",t)}paste(e){this._core.paste(e)}refresh(e,t){this._verifyIntegers(e,t),this._core.refresh(e,t)}reset(){this._core.reset()}clearTextureAtlas(){this._core.clearTextureAtlas()}loadAddon(e){this._addonManager.loadAddon(this,e)}static get strings(){return t}_verifyIntegers(...e){for(const t of e)if(t===1/0||isNaN(t)||t%1!=0)throw new Error("This API only accepts integers")}_verifyPositiveIntegers(...e){for(const t of e)if(t&&(t===1/0||isNaN(t)||t%1!=0||t<0))throw new Error("This API only accepts positive integers")}}e.Terminal=d})(),s})())); \ No newline at end of file diff --git a/static/xterm/xterm.min.js.new b/static/xterm/xterm.min.js.new new file mode 100644 index 0000000..d7bd63f --- /dev/null +++ b/static/xterm/xterm.min.js.new @@ -0,0 +1,8 @@ +/** + * Skipped minification because the original files appears to be already minified. + * Original file: /npm/xterm@5.3.0/lib/xterm.js + * + * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files + */ +!function(e,t){if("object"==typeof exports&&"object"==typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var i=t();for(var s in i)("object"==typeof exports?exports:e)[s]=i[s]}}(self,(()=>(()=>{"use strict";var e={4567:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.AccessibilityManager=void 0;const n=i(9042),o=i(6114),a=i(9924),h=i(844),c=i(5596),l=i(4725),d=i(3656);let _=t.AccessibilityManager=class extends h.Disposable{constructor(e,t){super(),this._terminal=e,this._renderService=t,this._liveRegionLineCount=0,this._charsToConsume=[],this._charsToAnnounce="",this._accessibilityContainer=document.createElement("div"),this._accessibilityContainer.classList.add("xterm-accessibility"),this._rowContainer=document.createElement("div"),this._rowContainer.setAttribute("role","list"),this._rowContainer.classList.add("xterm-accessibility-tree"),this._rowElements=[];for(let e=0;ethis._handleBoundaryFocus(e,0),this._bottomBoundaryFocusListener=e=>this._handleBoundaryFocus(e,1),this._rowElements[0].addEventListener("focus",this._topBoundaryFocusListener),this._rowElements[this._rowElements.length-1].addEventListener("focus",this._bottomBoundaryFocusListener),this._refreshRowsDimensions(),this._accessibilityContainer.appendChild(this._rowContainer),this._liveRegion=document.createElement("div"),this._liveRegion.classList.add("live-region"),this._liveRegion.setAttribute("aria-live","assertive"),this._accessibilityContainer.appendChild(this._liveRegion),this._liveRegionDebouncer=this.register(new a.TimeBasedDebouncer(this._renderRows.bind(this))),!this._terminal.element)throw new Error("Cannot enable accessibility before Terminal.open");this._terminal.element.insertAdjacentElement("afterbegin",this._accessibilityContainer),this.register(this._terminal.onResize((e=>this._handleResize(e.rows)))),this.register(this._terminal.onRender((e=>this._refreshRows(e.start,e.end)))),this.register(this._terminal.onScroll((()=>this._refreshRows()))),this.register(this._terminal.onA11yChar((e=>this._handleChar(e)))),this.register(this._terminal.onLineFeed((()=>this._handleChar("\n")))),this.register(this._terminal.onA11yTab((e=>this._handleTab(e)))),this.register(this._terminal.onKey((e=>this._handleKey(e.key)))),this.register(this._terminal.onBlur((()=>this._clearLiveRegion()))),this.register(this._renderService.onDimensionsChange((()=>this._refreshRowsDimensions()))),this._screenDprMonitor=new c.ScreenDprMonitor(window),this.register(this._screenDprMonitor),this._screenDprMonitor.setListener((()=>this._refreshRowsDimensions())),this.register((0,d.addDisposableDomListener)(window,"resize",(()=>this._refreshRowsDimensions()))),this._refreshRows(),this.register((0,h.toDisposable)((()=>{this._accessibilityContainer.remove(),this._rowElements.length=0})))}_handleTab(e){for(let t=0;t0?this._charsToConsume.shift()!==e&&(this._charsToAnnounce+=e):this._charsToAnnounce+=e,"\n"===e&&(this._liveRegionLineCount++,21===this._liveRegionLineCount&&(this._liveRegion.textContent+=n.tooMuchOutput)),o.isMac&&this._liveRegion.textContent&&this._liveRegion.textContent.length>0&&!this._liveRegion.parentNode&&setTimeout((()=>{this._accessibilityContainer.appendChild(this._liveRegion)}),0))}_clearLiveRegion(){this._liveRegion.textContent="",this._liveRegionLineCount=0,o.isMac&&this._liveRegion.remove()}_handleKey(e){this._clearLiveRegion(),/\p{Control}/u.test(e)||this._charsToConsume.push(e)}_refreshRows(e,t){this._liveRegionDebouncer.refresh(e,t,this._terminal.rows)}_renderRows(e,t){const i=this._terminal.buffer,s=i.lines.length.toString();for(let r=e;r<=t;r++){const e=i.translateBufferLineToString(i.ydisp+r,!0),t=(i.ydisp+r+1).toString(),n=this._rowElements[r];n&&(0===e.length?n.innerText=" ":n.textContent=e,n.setAttribute("aria-posinset",t),n.setAttribute("aria-setsize",s))}this._announceCharacters()}_announceCharacters(){0!==this._charsToAnnounce.length&&(this._liveRegion.textContent+=this._charsToAnnounce,this._charsToAnnounce="")}_handleBoundaryFocus(e,t){const i=e.target,s=this._rowElements[0===t?1:this._rowElements.length-2];if(i.getAttribute("aria-posinset")===(0===t?"1":`${this._terminal.buffer.lines.length}`))return;if(e.relatedTarget!==s)return;let r,n;if(0===t?(r=i,n=this._rowElements.pop(),this._rowContainer.removeChild(n)):(r=this._rowElements.shift(),n=i,this._rowContainer.removeChild(r)),r.removeEventListener("focus",this._topBoundaryFocusListener),n.removeEventListener("focus",this._bottomBoundaryFocusListener),0===t){const e=this._createAccessibilityTreeNode();this._rowElements.unshift(e),this._rowContainer.insertAdjacentElement("afterbegin",e)}else{const e=this._createAccessibilityTreeNode();this._rowElements.push(e),this._rowContainer.appendChild(e)}this._rowElements[0].addEventListener("focus",this._topBoundaryFocusListener),this._rowElements[this._rowElements.length-1].addEventListener("focus",this._bottomBoundaryFocusListener),this._terminal.scrollLines(0===t?-1:1),this._rowElements[0===t?1:this._rowElements.length-2].focus(),e.preventDefault(),e.stopImmediatePropagation()}_handleResize(e){this._rowElements[this._rowElements.length-1].removeEventListener("focus",this._bottomBoundaryFocusListener);for(let e=this._rowContainer.children.length;ee;)this._rowContainer.removeChild(this._rowElements.pop());this._rowElements[this._rowElements.length-1].addEventListener("focus",this._bottomBoundaryFocusListener),this._refreshRowsDimensions()}_createAccessibilityTreeNode(){const e=document.createElement("div");return e.setAttribute("role","listitem"),e.tabIndex=-1,this._refreshRowDimensions(e),e}_refreshRowsDimensions(){if(this._renderService.dimensions.css.cell.height){this._accessibilityContainer.style.width=`${this._renderService.dimensions.css.canvas.width}px`,this._rowElements.length!==this._terminal.rows&&this._handleResize(this._terminal.rows);for(let e=0;e{function i(e){return e.replace(/\r?\n/g,"\r")}function s(e,t){return t?"[200~"+e+"[201~":e}function r(e,t,r,n){e=s(e=i(e),r.decPrivateModes.bracketedPasteMode&&!0!==n.rawOptions.ignoreBracketedPasteMode),r.triggerDataEvent(e,!0),t.value=""}function n(e,t,i){const s=i.getBoundingClientRect(),r=e.clientX-s.left-10,n=e.clientY-s.top-10;t.style.width="20px",t.style.height="20px",t.style.left=`${r}px`,t.style.top=`${n}px`,t.style.zIndex="1000",t.focus()}Object.defineProperty(t,"__esModule",{value:!0}),t.rightClickHandler=t.moveTextAreaUnderMouseCursor=t.paste=t.handlePasteEvent=t.copyHandler=t.bracketTextForPaste=t.prepareTextForTerminal=void 0,t.prepareTextForTerminal=i,t.bracketTextForPaste=s,t.copyHandler=function(e,t){e.clipboardData&&e.clipboardData.setData("text/plain",t.selectionText),e.preventDefault()},t.handlePasteEvent=function(e,t,i,s){e.stopPropagation(),e.clipboardData&&r(e.clipboardData.getData("text/plain"),t,i,s)},t.paste=r,t.moveTextAreaUnderMouseCursor=n,t.rightClickHandler=function(e,t,i,s,r){n(e,t,i),r&&s.rightClickSelect(e),t.value=s.selectionText,t.select()}},7239:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.ColorContrastCache=void 0;const s=i(1505);t.ColorContrastCache=class{constructor(){this._color=new s.TwoKeyMap,this._css=new s.TwoKeyMap}setCss(e,t,i){this._css.set(e,t,i)}getCss(e,t){return this._css.get(e,t)}setColor(e,t,i){this._color.set(e,t,i)}getColor(e,t){return this._color.get(e,t)}clear(){this._color.clear(),this._css.clear()}}},3656:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.addDisposableDomListener=void 0,t.addDisposableDomListener=function(e,t,i,s){e.addEventListener(t,i,s);let r=!1;return{dispose:()=>{r||(r=!0,e.removeEventListener(t,i,s))}}}},6465:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.Linkifier2=void 0;const n=i(3656),o=i(8460),a=i(844),h=i(2585);let c=t.Linkifier2=class extends a.Disposable{get currentLink(){return this._currentLink}constructor(e){super(),this._bufferService=e,this._linkProviders=[],this._linkCacheDisposables=[],this._isMouseOut=!0,this._wasResized=!1,this._activeLine=-1,this._onShowLinkUnderline=this.register(new o.EventEmitter),this.onShowLinkUnderline=this._onShowLinkUnderline.event,this._onHideLinkUnderline=this.register(new o.EventEmitter),this.onHideLinkUnderline=this._onHideLinkUnderline.event,this.register((0,a.getDisposeArrayDisposable)(this._linkCacheDisposables)),this.register((0,a.toDisposable)((()=>{this._lastMouseEvent=void 0}))),this.register(this._bufferService.onResize((()=>{this._clearCurrentLink(),this._wasResized=!0})))}registerLinkProvider(e){return this._linkProviders.push(e),{dispose:()=>{const t=this._linkProviders.indexOf(e);-1!==t&&this._linkProviders.splice(t,1)}}}attachToDom(e,t,i){this._element=e,this._mouseService=t,this._renderService=i,this.register((0,n.addDisposableDomListener)(this._element,"mouseleave",(()=>{this._isMouseOut=!0,this._clearCurrentLink()}))),this.register((0,n.addDisposableDomListener)(this._element,"mousemove",this._handleMouseMove.bind(this))),this.register((0,n.addDisposableDomListener)(this._element,"mousedown",this._handleMouseDown.bind(this))),this.register((0,n.addDisposableDomListener)(this._element,"mouseup",this._handleMouseUp.bind(this)))}_handleMouseMove(e){if(this._lastMouseEvent=e,!this._element||!this._mouseService)return;const t=this._positionFromMouseEvent(e,this._element,this._mouseService);if(!t)return;this._isMouseOut=!1;const i=e.composedPath();for(let e=0;e{null==e||e.forEach((e=>{e.link.dispose&&e.link.dispose()}))})),this._activeProviderReplies=new Map,this._activeLine=e.y);let r=!1;for(const[i,n]of this._linkProviders.entries())t?(null===(s=this._activeProviderReplies)||void 0===s?void 0:s.get(i))&&(r=this._checkLinkProviderResult(i,e,r)):n.provideLinks(e.y,(t=>{var s,n;if(this._isMouseOut)return;const o=null==t?void 0:t.map((e=>({link:e})));null===(s=this._activeProviderReplies)||void 0===s||s.set(i,o),r=this._checkLinkProviderResult(i,e,r),(null===(n=this._activeProviderReplies)||void 0===n?void 0:n.size)===this._linkProviders.length&&this._removeIntersectingLinks(e.y,this._activeProviderReplies)}))}_removeIntersectingLinks(e,t){const i=new Set;for(let s=0;se?this._bufferService.cols:s.link.range.end.x;for(let e=n;e<=o;e++){if(i.has(e)){r.splice(t--,1);break}i.add(e)}}}}_checkLinkProviderResult(e,t,i){var s;if(!this._activeProviderReplies)return i;const r=this._activeProviderReplies.get(e);let n=!1;for(let t=0;tthis._linkAtPosition(e.link,t)));e&&(i=!0,this._handleNewLink(e))}if(this._activeProviderReplies.size===this._linkProviders.length&&!i)for(let e=0;ethis._linkAtPosition(e.link,t)));if(r){i=!0,this._handleNewLink(r);break}}return i}_handleMouseDown(){this._mouseDownLink=this._currentLink}_handleMouseUp(e){if(!this._element||!this._mouseService||!this._currentLink)return;const t=this._positionFromMouseEvent(e,this._element,this._mouseService);t&&this._mouseDownLink===this._currentLink&&this._linkAtPosition(this._currentLink.link,t)&&this._currentLink.link.activate(e,this._currentLink.link.text)}_clearCurrentLink(e,t){this._element&&this._currentLink&&this._lastMouseEvent&&(!e||!t||this._currentLink.link.range.start.y>=e&&this._currentLink.link.range.end.y<=t)&&(this._linkLeave(this._element,this._currentLink.link,this._lastMouseEvent),this._currentLink=void 0,(0,a.disposeArray)(this._linkCacheDisposables))}_handleNewLink(e){if(!this._element||!this._lastMouseEvent||!this._mouseService)return;const t=this._positionFromMouseEvent(this._lastMouseEvent,this._element,this._mouseService);t&&this._linkAtPosition(e.link,t)&&(this._currentLink=e,this._currentLink.state={decorations:{underline:void 0===e.link.decorations||e.link.decorations.underline,pointerCursor:void 0===e.link.decorations||e.link.decorations.pointerCursor},isHovered:!0},this._linkHover(this._element,e.link,this._lastMouseEvent),e.link.decorations={},Object.defineProperties(e.link.decorations,{pointerCursor:{get:()=>{var e,t;return null===(t=null===(e=this._currentLink)||void 0===e?void 0:e.state)||void 0===t?void 0:t.decorations.pointerCursor},set:e=>{var t,i;(null===(t=this._currentLink)||void 0===t?void 0:t.state)&&this._currentLink.state.decorations.pointerCursor!==e&&(this._currentLink.state.decorations.pointerCursor=e,this._currentLink.state.isHovered&&(null===(i=this._element)||void 0===i||i.classList.toggle("xterm-cursor-pointer",e)))}},underline:{get:()=>{var e,t;return null===(t=null===(e=this._currentLink)||void 0===e?void 0:e.state)||void 0===t?void 0:t.decorations.underline},set:t=>{var i,s,r;(null===(i=this._currentLink)||void 0===i?void 0:i.state)&&(null===(r=null===(s=this._currentLink)||void 0===s?void 0:s.state)||void 0===r?void 0:r.decorations.underline)!==t&&(this._currentLink.state.decorations.underline=t,this._currentLink.state.isHovered&&this._fireUnderlineEvent(e.link,t))}}}),this._renderService&&this._linkCacheDisposables.push(this._renderService.onRenderedViewportChange((e=>{if(!this._currentLink)return;const t=0===e.start?0:e.start+1+this._bufferService.buffer.ydisp,i=this._bufferService.buffer.ydisp+1+e.end;if(this._currentLink.link.range.start.y>=t&&this._currentLink.link.range.end.y<=i&&(this._clearCurrentLink(t,i),this._lastMouseEvent&&this._element)){const e=this._positionFromMouseEvent(this._lastMouseEvent,this._element,this._mouseService);e&&this._askForLink(e,!1)}}))))}_linkHover(e,t,i){var s;(null===(s=this._currentLink)||void 0===s?void 0:s.state)&&(this._currentLink.state.isHovered=!0,this._currentLink.state.decorations.underline&&this._fireUnderlineEvent(t,!0),this._currentLink.state.decorations.pointerCursor&&e.classList.add("xterm-cursor-pointer")),t.hover&&t.hover(i,t.text)}_fireUnderlineEvent(e,t){const i=e.range,s=this._bufferService.buffer.ydisp,r=this._createLinkUnderlineEvent(i.start.x-1,i.start.y-s-1,i.end.x,i.end.y-s-1,void 0);(t?this._onShowLinkUnderline:this._onHideLinkUnderline).fire(r)}_linkLeave(e,t,i){var s;(null===(s=this._currentLink)||void 0===s?void 0:s.state)&&(this._currentLink.state.isHovered=!1,this._currentLink.state.decorations.underline&&this._fireUnderlineEvent(t,!1),this._currentLink.state.decorations.pointerCursor&&e.classList.remove("xterm-cursor-pointer")),t.leave&&t.leave(i,t.text)}_linkAtPosition(e,t){const i=e.range.start.y*this._bufferService.cols+e.range.start.x,s=e.range.end.y*this._bufferService.cols+e.range.end.x,r=t.y*this._bufferService.cols+t.x;return i<=r&&r<=s}_positionFromMouseEvent(e,t,i){const s=i.getCoords(e,t,this._bufferService.cols,this._bufferService.rows);if(s)return{x:s[0],y:s[1]+this._bufferService.buffer.ydisp}}_createLinkUnderlineEvent(e,t,i,s,r){return{x1:e,y1:t,x2:i,y2:s,cols:this._bufferService.cols,fg:r}}};t.Linkifier2=c=s([r(0,h.IBufferService)],c)},9042:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.tooMuchOutput=t.promptLabel=void 0,t.promptLabel="Terminal input",t.tooMuchOutput="Too much output to announce, navigate to rows manually to read"},3730:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.OscLinkProvider=void 0;const n=i(511),o=i(2585);let a=t.OscLinkProvider=class{constructor(e,t,i){this._bufferService=e,this._optionsService=t,this._oscLinkService=i}provideLinks(e,t){var i;const s=this._bufferService.buffer.lines.get(e-1);if(!s)return void t(void 0);const r=[],o=this._optionsService.rawOptions.linkHandler,a=new n.CellData,c=s.getTrimmedLength();let l=-1,d=-1,_=!1;for(let t=0;to?o.activate(e,t,i):h(0,t),hover:(e,t)=>{var s;return null===(s=null==o?void 0:o.hover)||void 0===s?void 0:s.call(o,e,t,i)},leave:(e,t)=>{var s;return null===(s=null==o?void 0:o.leave)||void 0===s?void 0:s.call(o,e,t,i)}})}_=!1,a.hasExtendedAttrs()&&a.extended.urlId?(d=t,l=a.extended.urlId):(d=-1,l=-1)}}t(r)}};function h(e,t){if(confirm(`Do you want to navigate to ${t}?\n\nWARNING: This link could potentially be dangerous`)){const e=window.open();if(e){try{e.opener=null}catch(e){}e.location.href=t}else console.warn("Opening link blocked as opener could not be cleared")}}t.OscLinkProvider=a=s([r(0,o.IBufferService),r(1,o.IOptionsService),r(2,o.IOscLinkService)],a)},6193:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.RenderDebouncer=void 0,t.RenderDebouncer=class{constructor(e,t){this._parentWindow=e,this._renderCallback=t,this._refreshCallbacks=[]}dispose(){this._animationFrame&&(this._parentWindow.cancelAnimationFrame(this._animationFrame),this._animationFrame=void 0)}addRefreshCallback(e){return this._refreshCallbacks.push(e),this._animationFrame||(this._animationFrame=this._parentWindow.requestAnimationFrame((()=>this._innerRefresh()))),this._animationFrame}refresh(e,t,i){this._rowCount=i,e=void 0!==e?e:0,t=void 0!==t?t:this._rowCount-1,this._rowStart=void 0!==this._rowStart?Math.min(this._rowStart,e):e,this._rowEnd=void 0!==this._rowEnd?Math.max(this._rowEnd,t):t,this._animationFrame||(this._animationFrame=this._parentWindow.requestAnimationFrame((()=>this._innerRefresh())))}_innerRefresh(){if(this._animationFrame=void 0,void 0===this._rowStart||void 0===this._rowEnd||void 0===this._rowCount)return void this._runRefreshCallbacks();const e=Math.max(this._rowStart,0),t=Math.min(this._rowEnd,this._rowCount-1);this._rowStart=void 0,this._rowEnd=void 0,this._renderCallback(e,t),this._runRefreshCallbacks()}_runRefreshCallbacks(){for(const e of this._refreshCallbacks)e(0);this._refreshCallbacks=[]}}},5596:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.ScreenDprMonitor=void 0;const s=i(844);class r extends s.Disposable{constructor(e){super(),this._parentWindow=e,this._currentDevicePixelRatio=this._parentWindow.devicePixelRatio,this.register((0,s.toDisposable)((()=>{this.clearListener()})))}setListener(e){this._listener&&this.clearListener(),this._listener=e,this._outerListener=()=>{this._listener&&(this._listener(this._parentWindow.devicePixelRatio,this._currentDevicePixelRatio),this._updateDpr())},this._updateDpr()}_updateDpr(){var e;this._outerListener&&(null===(e=this._resolutionMediaMatchList)||void 0===e||e.removeListener(this._outerListener),this._currentDevicePixelRatio=this._parentWindow.devicePixelRatio,this._resolutionMediaMatchList=this._parentWindow.matchMedia(`screen and (resolution: ${this._parentWindow.devicePixelRatio}dppx)`),this._resolutionMediaMatchList.addListener(this._outerListener))}clearListener(){this._resolutionMediaMatchList&&this._listener&&this._outerListener&&(this._resolutionMediaMatchList.removeListener(this._outerListener),this._resolutionMediaMatchList=void 0,this._listener=void 0,this._outerListener=void 0)}}t.ScreenDprMonitor=r},3236:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.Terminal=void 0;const s=i(3614),r=i(3656),n=i(6465),o=i(9042),a=i(3730),h=i(1680),c=i(3107),l=i(5744),d=i(2950),_=i(1296),u=i(428),f=i(4269),v=i(5114),p=i(8934),g=i(3230),m=i(9312),S=i(4725),C=i(6731),b=i(8055),y=i(8969),w=i(8460),E=i(844),k=i(6114),L=i(8437),D=i(2584),R=i(7399),x=i(5941),A=i(9074),B=i(2585),T=i(5435),M=i(4567),O="undefined"!=typeof window?window.document:null;class P extends y.CoreTerminal{get onFocus(){return this._onFocus.event}get onBlur(){return this._onBlur.event}get onA11yChar(){return this._onA11yCharEmitter.event}get onA11yTab(){return this._onA11yTabEmitter.event}get onWillOpen(){return this._onWillOpen.event}constructor(e={}){super(e),this.browser=k,this._keyDownHandled=!1,this._keyDownSeen=!1,this._keyPressHandled=!1,this._unprocessedDeadKey=!1,this._accessibilityManager=this.register(new E.MutableDisposable),this._onCursorMove=this.register(new w.EventEmitter),this.onCursorMove=this._onCursorMove.event,this._onKey=this.register(new w.EventEmitter),this.onKey=this._onKey.event,this._onRender=this.register(new w.EventEmitter),this.onRender=this._onRender.event,this._onSelectionChange=this.register(new w.EventEmitter),this.onSelectionChange=this._onSelectionChange.event,this._onTitleChange=this.register(new w.EventEmitter),this.onTitleChange=this._onTitleChange.event,this._onBell=this.register(new w.EventEmitter),this.onBell=this._onBell.event,this._onFocus=this.register(new w.EventEmitter),this._onBlur=this.register(new w.EventEmitter),this._onA11yCharEmitter=this.register(new w.EventEmitter),this._onA11yTabEmitter=this.register(new w.EventEmitter),this._onWillOpen=this.register(new w.EventEmitter),this._setup(),this.linkifier2=this.register(this._instantiationService.createInstance(n.Linkifier2)),this.linkifier2.registerLinkProvider(this._instantiationService.createInstance(a.OscLinkProvider)),this._decorationService=this._instantiationService.createInstance(A.DecorationService),this._instantiationService.setService(B.IDecorationService,this._decorationService),this.register(this._inputHandler.onRequestBell((()=>this._onBell.fire()))),this.register(this._inputHandler.onRequestRefreshRows(((e,t)=>this.refresh(e,t)))),this.register(this._inputHandler.onRequestSendFocus((()=>this._reportFocus()))),this.register(this._inputHandler.onRequestReset((()=>this.reset()))),this.register(this._inputHandler.onRequestWindowsOptionsReport((e=>this._reportWindowsOptions(e)))),this.register(this._inputHandler.onColor((e=>this._handleColorEvent(e)))),this.register((0,w.forwardEvent)(this._inputHandler.onCursorMove,this._onCursorMove)),this.register((0,w.forwardEvent)(this._inputHandler.onTitleChange,this._onTitleChange)),this.register((0,w.forwardEvent)(this._inputHandler.onA11yChar,this._onA11yCharEmitter)),this.register((0,w.forwardEvent)(this._inputHandler.onA11yTab,this._onA11yTabEmitter)),this.register(this._bufferService.onResize((e=>this._afterResize(e.cols,e.rows)))),this.register((0,E.toDisposable)((()=>{var e,t;this._customKeyEventHandler=void 0,null===(t=null===(e=this.element)||void 0===e?void 0:e.parentNode)||void 0===t||t.removeChild(this.element)})))}_handleColorEvent(e){if(this._themeService)for(const t of e){let e,i="";switch(t.index){case 256:e="foreground",i="10";break;case 257:e="background",i="11";break;case 258:e="cursor",i="12";break;default:e="ansi",i="4;"+t.index}switch(t.type){case 0:const s=b.color.toColorRGB("ansi"===e?this._themeService.colors.ansi[t.index]:this._themeService.colors[e]);this.coreService.triggerDataEvent(`${D.C0.ESC}]${i};${(0,x.toRgbString)(s)}${D.C1_ESCAPED.ST}`);break;case 1:if("ansi"===e)this._themeService.modifyColors((e=>e.ansi[t.index]=b.rgba.toColor(...t.color)));else{const i=e;this._themeService.modifyColors((e=>e[i]=b.rgba.toColor(...t.color)))}break;case 2:this._themeService.restoreColor(t.index)}}}_setup(){super._setup(),this._customKeyEventHandler=void 0}get buffer(){return this.buffers.active}focus(){this.textarea&&this.textarea.focus({preventScroll:!0})}_handleScreenReaderModeOptionChange(e){e?!this._accessibilityManager.value&&this._renderService&&(this._accessibilityManager.value=this._instantiationService.createInstance(M.AccessibilityManager,this)):this._accessibilityManager.clear()}_handleTextAreaFocus(e){this.coreService.decPrivateModes.sendFocus&&this.coreService.triggerDataEvent(D.C0.ESC+"[I"),this.updateCursorStyle(e),this.element.classList.add("focus"),this._showCursor(),this._onFocus.fire()}blur(){var e;return null===(e=this.textarea)||void 0===e?void 0:e.blur()}_handleTextAreaBlur(){this.textarea.value="",this.refresh(this.buffer.y,this.buffer.y),this.coreService.decPrivateModes.sendFocus&&this.coreService.triggerDataEvent(D.C0.ESC+"[O"),this.element.classList.remove("focus"),this._onBlur.fire()}_syncTextArea(){if(!this.textarea||!this.buffer.isCursorInViewport||this._compositionHelper.isComposing||!this._renderService)return;const e=this.buffer.ybase+this.buffer.y,t=this.buffer.lines.get(e);if(!t)return;const i=Math.min(this.buffer.x,this.cols-1),s=this._renderService.dimensions.css.cell.height,r=t.getWidth(i),n=this._renderService.dimensions.css.cell.width*r,o=this.buffer.y*this._renderService.dimensions.css.cell.height,a=i*this._renderService.dimensions.css.cell.width;this.textarea.style.left=a+"px",this.textarea.style.top=o+"px",this.textarea.style.width=n+"px",this.textarea.style.height=s+"px",this.textarea.style.lineHeight=s+"px",this.textarea.style.zIndex="-5"}_initGlobal(){this._bindKeys(),this.register((0,r.addDisposableDomListener)(this.element,"copy",(e=>{this.hasSelection()&&(0,s.copyHandler)(e,this._selectionService)})));const e=e=>(0,s.handlePasteEvent)(e,this.textarea,this.coreService,this.optionsService);this.register((0,r.addDisposableDomListener)(this.textarea,"paste",e)),this.register((0,r.addDisposableDomListener)(this.element,"paste",e)),k.isFirefox?this.register((0,r.addDisposableDomListener)(this.element,"mousedown",(e=>{2===e.button&&(0,s.rightClickHandler)(e,this.textarea,this.screenElement,this._selectionService,this.options.rightClickSelectsWord)}))):this.register((0,r.addDisposableDomListener)(this.element,"contextmenu",(e=>{(0,s.rightClickHandler)(e,this.textarea,this.screenElement,this._selectionService,this.options.rightClickSelectsWord)}))),k.isLinux&&this.register((0,r.addDisposableDomListener)(this.element,"auxclick",(e=>{1===e.button&&(0,s.moveTextAreaUnderMouseCursor)(e,this.textarea,this.screenElement)})))}_bindKeys(){this.register((0,r.addDisposableDomListener)(this.textarea,"keyup",(e=>this._keyUp(e)),!0)),this.register((0,r.addDisposableDomListener)(this.textarea,"keydown",(e=>this._keyDown(e)),!0)),this.register((0,r.addDisposableDomListener)(this.textarea,"keypress",(e=>this._keyPress(e)),!0)),this.register((0,r.addDisposableDomListener)(this.textarea,"compositionstart",(()=>this._compositionHelper.compositionstart()))),this.register((0,r.addDisposableDomListener)(this.textarea,"compositionupdate",(e=>this._compositionHelper.compositionupdate(e)))),this.register((0,r.addDisposableDomListener)(this.textarea,"compositionend",(()=>this._compositionHelper.compositionend()))),this.register((0,r.addDisposableDomListener)(this.textarea,"input",(e=>this._inputEvent(e)),!0)),this.register(this.onRender((()=>this._compositionHelper.updateCompositionElements())))}open(e){var t;if(!e)throw new Error("Terminal requires a parent element.");e.isConnected||this._logService.debug("Terminal.open was called on an element that was not attached to the DOM"),this._document=e.ownerDocument,this.element=this._document.createElement("div"),this.element.dir="ltr",this.element.classList.add("terminal"),this.element.classList.add("xterm"),e.appendChild(this.element);const i=O.createDocumentFragment();this._viewportElement=O.createElement("div"),this._viewportElement.classList.add("xterm-viewport"),i.appendChild(this._viewportElement),this._viewportScrollArea=O.createElement("div"),this._viewportScrollArea.classList.add("xterm-scroll-area"),this._viewportElement.appendChild(this._viewportScrollArea),this.screenElement=O.createElement("div"),this.screenElement.classList.add("xterm-screen"),this._helperContainer=O.createElement("div"),this._helperContainer.classList.add("xterm-helpers"),this.screenElement.appendChild(this._helperContainer),i.appendChild(this.screenElement),this.textarea=O.createElement("textarea"),this.textarea.classList.add("xterm-helper-textarea"),this.textarea.setAttribute("aria-label",o.promptLabel),k.isChromeOS||this.textarea.setAttribute("aria-multiline","false"),this.textarea.setAttribute("autocorrect","off"),this.textarea.setAttribute("autocapitalize","off"),this.textarea.setAttribute("spellcheck","false"),this.textarea.tabIndex=0,this._coreBrowserService=this._instantiationService.createInstance(v.CoreBrowserService,this.textarea,null!==(t=this._document.defaultView)&&void 0!==t?t:window),this._instantiationService.setService(S.ICoreBrowserService,this._coreBrowserService),this.register((0,r.addDisposableDomListener)(this.textarea,"focus",(e=>this._handleTextAreaFocus(e)))),this.register((0,r.addDisposableDomListener)(this.textarea,"blur",(()=>this._handleTextAreaBlur()))),this._helperContainer.appendChild(this.textarea),this._charSizeService=this._instantiationService.createInstance(u.CharSizeService,this._document,this._helperContainer),this._instantiationService.setService(S.ICharSizeService,this._charSizeService),this._themeService=this._instantiationService.createInstance(C.ThemeService),this._instantiationService.setService(S.IThemeService,this._themeService),this._characterJoinerService=this._instantiationService.createInstance(f.CharacterJoinerService),this._instantiationService.setService(S.ICharacterJoinerService,this._characterJoinerService),this._renderService=this.register(this._instantiationService.createInstance(g.RenderService,this.rows,this.screenElement)),this._instantiationService.setService(S.IRenderService,this._renderService),this.register(this._renderService.onRenderedViewportChange((e=>this._onRender.fire(e)))),this.onResize((e=>this._renderService.resize(e.cols,e.rows))),this._compositionView=O.createElement("div"),this._compositionView.classList.add("composition-view"),this._compositionHelper=this._instantiationService.createInstance(d.CompositionHelper,this.textarea,this._compositionView),this._helperContainer.appendChild(this._compositionView),this.element.appendChild(i);try{this._onWillOpen.fire(this.element)}catch(e){}this._renderService.hasRenderer()||this._renderService.setRenderer(this._createRenderer()),this._mouseService=this._instantiationService.createInstance(p.MouseService),this._instantiationService.setService(S.IMouseService,this._mouseService),this.viewport=this._instantiationService.createInstance(h.Viewport,this._viewportElement,this._viewportScrollArea),this.viewport.onRequestScrollLines((e=>this.scrollLines(e.amount,e.suppressScrollEvent,1))),this.register(this._inputHandler.onRequestSyncScrollBar((()=>this.viewport.syncScrollArea()))),this.register(this.viewport),this.register(this.onCursorMove((()=>{this._renderService.handleCursorMove(),this._syncTextArea()}))),this.register(this.onResize((()=>this._renderService.handleResize(this.cols,this.rows)))),this.register(this.onBlur((()=>this._renderService.handleBlur()))),this.register(this.onFocus((()=>this._renderService.handleFocus()))),this.register(this._renderService.onDimensionsChange((()=>this.viewport.syncScrollArea()))),this._selectionService=this.register(this._instantiationService.createInstance(m.SelectionService,this.element,this.screenElement,this.linkifier2)),this._instantiationService.setService(S.ISelectionService,this._selectionService),this.register(this._selectionService.onRequestScrollLines((e=>this.scrollLines(e.amount,e.suppressScrollEvent)))),this.register(this._selectionService.onSelectionChange((()=>this._onSelectionChange.fire()))),this.register(this._selectionService.onRequestRedraw((e=>this._renderService.handleSelectionChanged(e.start,e.end,e.columnSelectMode)))),this.register(this._selectionService.onLinuxMouseSelection((e=>{this.textarea.value=e,this.textarea.focus(),this.textarea.select()}))),this.register(this._onScroll.event((e=>{this.viewport.syncScrollArea(),this._selectionService.refresh()}))),this.register((0,r.addDisposableDomListener)(this._viewportElement,"scroll",(()=>this._selectionService.refresh()))),this.linkifier2.attachToDom(this.screenElement,this._mouseService,this._renderService),this.register(this._instantiationService.createInstance(c.BufferDecorationRenderer,this.screenElement)),this.register((0,r.addDisposableDomListener)(this.element,"mousedown",(e=>this._selectionService.handleMouseDown(e)))),this.coreMouseService.areMouseEventsActive?(this._selectionService.disable(),this.element.classList.add("enable-mouse-events")):this._selectionService.enable(),this.options.screenReaderMode&&(this._accessibilityManager.value=this._instantiationService.createInstance(M.AccessibilityManager,this)),this.register(this.optionsService.onSpecificOptionChange("screenReaderMode",(e=>this._handleScreenReaderModeOptionChange(e)))),this.options.overviewRulerWidth&&(this._overviewRulerRenderer=this.register(this._instantiationService.createInstance(l.OverviewRulerRenderer,this._viewportElement,this.screenElement))),this.optionsService.onSpecificOptionChange("overviewRulerWidth",(e=>{!this._overviewRulerRenderer&&e&&this._viewportElement&&this.screenElement&&(this._overviewRulerRenderer=this.register(this._instantiationService.createInstance(l.OverviewRulerRenderer,this._viewportElement,this.screenElement)))})),this._charSizeService.measure(),this.refresh(0,this.rows-1),this._initGlobal(),this.bindMouse()}_createRenderer(){return this._instantiationService.createInstance(_.DomRenderer,this.element,this.screenElement,this._viewportElement,this.linkifier2)}bindMouse(){const e=this,t=this.element;function i(t){const i=e._mouseService.getMouseReportCoords(t,e.screenElement);if(!i)return!1;let s,r;switch(t.overrideType||t.type){case"mousemove":r=32,void 0===t.buttons?(s=3,void 0!==t.button&&(s=t.button<3?t.button:3)):s=1&t.buttons?0:4&t.buttons?1:2&t.buttons?2:3;break;case"mouseup":r=0,s=t.button<3?t.button:3;break;case"mousedown":r=1,s=t.button<3?t.button:3;break;case"wheel":if(0===e.viewport.getLinesScrolled(t))return!1;r=t.deltaY<0?0:1,s=4;break;default:return!1}return!(void 0===r||void 0===s||s>4)&&e.coreMouseService.triggerMouseEvent({col:i.col,row:i.row,x:i.x,y:i.y,button:s,action:r,ctrl:t.ctrlKey,alt:t.altKey,shift:t.shiftKey})}const s={mouseup:null,wheel:null,mousedrag:null,mousemove:null},n={mouseup:e=>(i(e),e.buttons||(this._document.removeEventListener("mouseup",s.mouseup),s.mousedrag&&this._document.removeEventListener("mousemove",s.mousedrag)),this.cancel(e)),wheel:e=>(i(e),this.cancel(e,!0)),mousedrag:e=>{e.buttons&&i(e)},mousemove:e=>{e.buttons||i(e)}};this.register(this.coreMouseService.onProtocolChange((e=>{e?("debug"===this.optionsService.rawOptions.logLevel&&this._logService.debug("Binding to mouse events:",this.coreMouseService.explainEvents(e)),this.element.classList.add("enable-mouse-events"),this._selectionService.disable()):(this._logService.debug("Unbinding from mouse events."),this.element.classList.remove("enable-mouse-events"),this._selectionService.enable()),8&e?s.mousemove||(t.addEventListener("mousemove",n.mousemove),s.mousemove=n.mousemove):(t.removeEventListener("mousemove",s.mousemove),s.mousemove=null),16&e?s.wheel||(t.addEventListener("wheel",n.wheel,{passive:!1}),s.wheel=n.wheel):(t.removeEventListener("wheel",s.wheel),s.wheel=null),2&e?s.mouseup||(t.addEventListener("mouseup",n.mouseup),s.mouseup=n.mouseup):(this._document.removeEventListener("mouseup",s.mouseup),t.removeEventListener("mouseup",s.mouseup),s.mouseup=null),4&e?s.mousedrag||(s.mousedrag=n.mousedrag):(this._document.removeEventListener("mousemove",s.mousedrag),s.mousedrag=null)}))),this.coreMouseService.activeProtocol=this.coreMouseService.activeProtocol,this.register((0,r.addDisposableDomListener)(t,"mousedown",(e=>{if(e.preventDefault(),this.focus(),this.coreMouseService.areMouseEventsActive&&!this._selectionService.shouldForceSelection(e))return i(e),s.mouseup&&this._document.addEventListener("mouseup",s.mouseup),s.mousedrag&&this._document.addEventListener("mousemove",s.mousedrag),this.cancel(e)}))),this.register((0,r.addDisposableDomListener)(t,"wheel",(e=>{if(!s.wheel){if(!this.buffer.hasScrollback){const t=this.viewport.getLinesScrolled(e);if(0===t)return;const i=D.C0.ESC+(this.coreService.decPrivateModes.applicationCursorKeys?"O":"[")+(e.deltaY<0?"A":"B");let s="";for(let e=0;e{if(!this.coreMouseService.areMouseEventsActive)return this.viewport.handleTouchStart(e),this.cancel(e)}),{passive:!0})),this.register((0,r.addDisposableDomListener)(t,"touchmove",(e=>{if(!this.coreMouseService.areMouseEventsActive)return this.viewport.handleTouchMove(e)?void 0:this.cancel(e)}),{passive:!1}))}refresh(e,t){var i;null===(i=this._renderService)||void 0===i||i.refreshRows(e,t)}updateCursorStyle(e){var t;(null===(t=this._selectionService)||void 0===t?void 0:t.shouldColumnSelect(e))?this.element.classList.add("column-select"):this.element.classList.remove("column-select")}_showCursor(){this.coreService.isCursorInitialized||(this.coreService.isCursorInitialized=!0,this.refresh(this.buffer.y,this.buffer.y))}scrollLines(e,t,i=0){var s;1===i?(super.scrollLines(e,t,i),this.refresh(0,this.rows-1)):null===(s=this.viewport)||void 0===s||s.scrollLines(e)}paste(e){(0,s.paste)(e,this.textarea,this.coreService,this.optionsService)}attachCustomKeyEventHandler(e){this._customKeyEventHandler=e}registerLinkProvider(e){return this.linkifier2.registerLinkProvider(e)}registerCharacterJoiner(e){if(!this._characterJoinerService)throw new Error("Terminal must be opened first");const t=this._characterJoinerService.register(e);return this.refresh(0,this.rows-1),t}deregisterCharacterJoiner(e){if(!this._characterJoinerService)throw new Error("Terminal must be opened first");this._characterJoinerService.deregister(e)&&this.refresh(0,this.rows-1)}get markers(){return this.buffer.markers}registerMarker(e){return this.buffer.addMarker(this.buffer.ybase+this.buffer.y+e)}registerDecoration(e){return this._decorationService.registerDecoration(e)}hasSelection(){return!!this._selectionService&&this._selectionService.hasSelection}select(e,t,i){this._selectionService.setSelection(e,t,i)}getSelection(){return this._selectionService?this._selectionService.selectionText:""}getSelectionPosition(){if(this._selectionService&&this._selectionService.hasSelection)return{start:{x:this._selectionService.selectionStart[0],y:this._selectionService.selectionStart[1]},end:{x:this._selectionService.selectionEnd[0],y:this._selectionService.selectionEnd[1]}}}clearSelection(){var e;null===(e=this._selectionService)||void 0===e||e.clearSelection()}selectAll(){var e;null===(e=this._selectionService)||void 0===e||e.selectAll()}selectLines(e,t){var i;null===(i=this._selectionService)||void 0===i||i.selectLines(e,t)}_keyDown(e){if(this._keyDownHandled=!1,this._keyDownSeen=!0,this._customKeyEventHandler&&!1===this._customKeyEventHandler(e))return!1;const t=this.browser.isMac&&this.options.macOptionIsMeta&&e.altKey;if(!t&&!this._compositionHelper.keydown(e))return this.options.scrollOnUserInput&&this.buffer.ybase!==this.buffer.ydisp&&this.scrollToBottom(),!1;t||"Dead"!==e.key&&"AltGraph"!==e.key||(this._unprocessedDeadKey=!0);const i=(0,R.evaluateKeyboardEvent)(e,this.coreService.decPrivateModes.applicationCursorKeys,this.browser.isMac,this.options.macOptionIsMeta);if(this.updateCursorStyle(e),3===i.type||2===i.type){const t=this.rows-1;return this.scrollLines(2===i.type?-t:t),this.cancel(e,!0)}return 1===i.type&&this.selectAll(),!!this._isThirdLevelShift(this.browser,e)||(i.cancel&&this.cancel(e,!0),!i.key||!!(e.key&&!e.ctrlKey&&!e.altKey&&!e.metaKey&&1===e.key.length&&e.key.charCodeAt(0)>=65&&e.key.charCodeAt(0)<=90)||(this._unprocessedDeadKey?(this._unprocessedDeadKey=!1,!0):(i.key!==D.C0.ETX&&i.key!==D.C0.CR||(this.textarea.value=""),this._onKey.fire({key:i.key,domEvent:e}),this._showCursor(),this.coreService.triggerDataEvent(i.key,!0),!this.optionsService.rawOptions.screenReaderMode||e.altKey||e.ctrlKey?this.cancel(e,!0):void(this._keyDownHandled=!0))))}_isThirdLevelShift(e,t){const i=e.isMac&&!this.options.macOptionIsMeta&&t.altKey&&!t.ctrlKey&&!t.metaKey||e.isWindows&&t.altKey&&t.ctrlKey&&!t.metaKey||e.isWindows&&t.getModifierState("AltGraph");return"keypress"===t.type?i:i&&(!t.keyCode||t.keyCode>47)}_keyUp(e){this._keyDownSeen=!1,this._customKeyEventHandler&&!1===this._customKeyEventHandler(e)||(function(e){return 16===e.keyCode||17===e.keyCode||18===e.keyCode}(e)||this.focus(),this.updateCursorStyle(e),this._keyPressHandled=!1)}_keyPress(e){let t;if(this._keyPressHandled=!1,this._keyDownHandled)return!1;if(this._customKeyEventHandler&&!1===this._customKeyEventHandler(e))return!1;if(this.cancel(e),e.charCode)t=e.charCode;else if(null===e.which||void 0===e.which)t=e.keyCode;else{if(0===e.which||0===e.charCode)return!1;t=e.which}return!(!t||(e.altKey||e.ctrlKey||e.metaKey)&&!this._isThirdLevelShift(this.browser,e)||(t=String.fromCharCode(t),this._onKey.fire({key:t,domEvent:e}),this._showCursor(),this.coreService.triggerDataEvent(t,!0),this._keyPressHandled=!0,this._unprocessedDeadKey=!1,0))}_inputEvent(e){if(e.data&&"insertText"===e.inputType&&(!e.composed||!this._keyDownSeen)&&!this.optionsService.rawOptions.screenReaderMode){if(this._keyPressHandled)return!1;this._unprocessedDeadKey=!1;const t=e.data;return this.coreService.triggerDataEvent(t,!0),this.cancel(e),!0}return!1}resize(e,t){e!==this.cols||t!==this.rows?super.resize(e,t):this._charSizeService&&!this._charSizeService.hasValidSize&&this._charSizeService.measure()}_afterResize(e,t){var i,s;null===(i=this._charSizeService)||void 0===i||i.measure(),null===(s=this.viewport)||void 0===s||s.syncScrollArea(!0)}clear(){var e;if(0!==this.buffer.ybase||0!==this.buffer.y){this.buffer.clearAllMarkers(),this.buffer.lines.set(0,this.buffer.lines.get(this.buffer.ybase+this.buffer.y)),this.buffer.lines.length=1,this.buffer.ydisp=0,this.buffer.ybase=0,this.buffer.y=0;for(let e=1;e{Object.defineProperty(t,"__esModule",{value:!0}),t.TimeBasedDebouncer=void 0,t.TimeBasedDebouncer=class{constructor(e,t=1e3){this._renderCallback=e,this._debounceThresholdMS=t,this._lastRefreshMs=0,this._additionalRefreshRequested=!1}dispose(){this._refreshTimeoutID&&clearTimeout(this._refreshTimeoutID)}refresh(e,t,i){this._rowCount=i,e=void 0!==e?e:0,t=void 0!==t?t:this._rowCount-1,this._rowStart=void 0!==this._rowStart?Math.min(this._rowStart,e):e,this._rowEnd=void 0!==this._rowEnd?Math.max(this._rowEnd,t):t;const s=Date.now();if(s-this._lastRefreshMs>=this._debounceThresholdMS)this._lastRefreshMs=s,this._innerRefresh();else if(!this._additionalRefreshRequested){const e=s-this._lastRefreshMs,t=this._debounceThresholdMS-e;this._additionalRefreshRequested=!0,this._refreshTimeoutID=window.setTimeout((()=>{this._lastRefreshMs=Date.now(),this._innerRefresh(),this._additionalRefreshRequested=!1,this._refreshTimeoutID=void 0}),t)}}_innerRefresh(){if(void 0===this._rowStart||void 0===this._rowEnd||void 0===this._rowCount)return;const e=Math.max(this._rowStart,0),t=Math.min(this._rowEnd,this._rowCount-1);this._rowStart=void 0,this._rowEnd=void 0,this._renderCallback(e,t)}}},1680:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.Viewport=void 0;const n=i(3656),o=i(4725),a=i(8460),h=i(844),c=i(2585);let l=t.Viewport=class extends h.Disposable{constructor(e,t,i,s,r,o,h,c){super(),this._viewportElement=e,this._scrollArea=t,this._bufferService=i,this._optionsService=s,this._charSizeService=r,this._renderService=o,this._coreBrowserService=h,this.scrollBarWidth=0,this._currentRowHeight=0,this._currentDeviceCellHeight=0,this._lastRecordedBufferLength=0,this._lastRecordedViewportHeight=0,this._lastRecordedBufferHeight=0,this._lastTouchY=0,this._lastScrollTop=0,this._wheelPartialScroll=0,this._refreshAnimationFrame=null,this._ignoreNextScrollEvent=!1,this._smoothScrollState={startTime:0,origin:-1,target:-1},this._onRequestScrollLines=this.register(new a.EventEmitter),this.onRequestScrollLines=this._onRequestScrollLines.event,this.scrollBarWidth=this._viewportElement.offsetWidth-this._scrollArea.offsetWidth||15,this.register((0,n.addDisposableDomListener)(this._viewportElement,"scroll",this._handleScroll.bind(this))),this._activeBuffer=this._bufferService.buffer,this.register(this._bufferService.buffers.onBufferActivate((e=>this._activeBuffer=e.activeBuffer))),this._renderDimensions=this._renderService.dimensions,this.register(this._renderService.onDimensionsChange((e=>this._renderDimensions=e))),this._handleThemeChange(c.colors),this.register(c.onChangeColors((e=>this._handleThemeChange(e)))),this.register(this._optionsService.onSpecificOptionChange("scrollback",(()=>this.syncScrollArea()))),setTimeout((()=>this.syncScrollArea()))}_handleThemeChange(e){this._viewportElement.style.backgroundColor=e.background.css}reset(){this._currentRowHeight=0,this._currentDeviceCellHeight=0,this._lastRecordedBufferLength=0,this._lastRecordedViewportHeight=0,this._lastRecordedBufferHeight=0,this._lastTouchY=0,this._lastScrollTop=0,this._coreBrowserService.window.requestAnimationFrame((()=>this.syncScrollArea()))}_refresh(e){if(e)return this._innerRefresh(),void(null!==this._refreshAnimationFrame&&this._coreBrowserService.window.cancelAnimationFrame(this._refreshAnimationFrame));null===this._refreshAnimationFrame&&(this._refreshAnimationFrame=this._coreBrowserService.window.requestAnimationFrame((()=>this._innerRefresh())))}_innerRefresh(){if(this._charSizeService.height>0){this._currentRowHeight=this._renderService.dimensions.device.cell.height/this._coreBrowserService.dpr,this._currentDeviceCellHeight=this._renderService.dimensions.device.cell.height,this._lastRecordedViewportHeight=this._viewportElement.offsetHeight;const e=Math.round(this._currentRowHeight*this._lastRecordedBufferLength)+(this._lastRecordedViewportHeight-this._renderService.dimensions.css.canvas.height);this._lastRecordedBufferHeight!==e&&(this._lastRecordedBufferHeight=e,this._scrollArea.style.height=this._lastRecordedBufferHeight+"px")}const e=this._bufferService.buffer.ydisp*this._currentRowHeight;this._viewportElement.scrollTop!==e&&(this._ignoreNextScrollEvent=!0,this._viewportElement.scrollTop=e),this._refreshAnimationFrame=null}syncScrollArea(e=!1){if(this._lastRecordedBufferLength!==this._bufferService.buffer.lines.length)return this._lastRecordedBufferLength=this._bufferService.buffer.lines.length,void this._refresh(e);this._lastRecordedViewportHeight===this._renderService.dimensions.css.canvas.height&&this._lastScrollTop===this._activeBuffer.ydisp*this._currentRowHeight&&this._renderDimensions.device.cell.height===this._currentDeviceCellHeight||this._refresh(e)}_handleScroll(e){if(this._lastScrollTop=this._viewportElement.scrollTop,!this._viewportElement.offsetParent)return;if(this._ignoreNextScrollEvent)return this._ignoreNextScrollEvent=!1,void this._onRequestScrollLines.fire({amount:0,suppressScrollEvent:!0});const t=Math.round(this._lastScrollTop/this._currentRowHeight)-this._bufferService.buffer.ydisp;this._onRequestScrollLines.fire({amount:t,suppressScrollEvent:!0})}_smoothScroll(){if(this._isDisposed||-1===this._smoothScrollState.origin||-1===this._smoothScrollState.target)return;const e=this._smoothScrollPercent();this._viewportElement.scrollTop=this._smoothScrollState.origin+Math.round(e*(this._smoothScrollState.target-this._smoothScrollState.origin)),e<1?this._coreBrowserService.window.requestAnimationFrame((()=>this._smoothScroll())):this._clearSmoothScrollState()}_smoothScrollPercent(){return this._optionsService.rawOptions.smoothScrollDuration&&this._smoothScrollState.startTime?Math.max(Math.min((Date.now()-this._smoothScrollState.startTime)/this._optionsService.rawOptions.smoothScrollDuration,1),0):1}_clearSmoothScrollState(){this._smoothScrollState.startTime=0,this._smoothScrollState.origin=-1,this._smoothScrollState.target=-1}_bubbleScroll(e,t){const i=this._viewportElement.scrollTop+this._lastRecordedViewportHeight;return!(t<0&&0!==this._viewportElement.scrollTop||t>0&&i0&&(s=e),r=""}}return{bufferElements:n,cursorElement:s}}getLinesScrolled(e){if(0===e.deltaY||e.shiftKey)return 0;let t=this._applyScrollModifier(e.deltaY,e);return e.deltaMode===WheelEvent.DOM_DELTA_PIXEL?(t/=this._currentRowHeight+0,this._wheelPartialScroll+=t,t=Math.floor(Math.abs(this._wheelPartialScroll))*(this._wheelPartialScroll>0?1:-1),this._wheelPartialScroll%=1):e.deltaMode===WheelEvent.DOM_DELTA_PAGE&&(t*=this._bufferService.rows),t}_applyScrollModifier(e,t){const i=this._optionsService.rawOptions.fastScrollModifier;return"alt"===i&&t.altKey||"ctrl"===i&&t.ctrlKey||"shift"===i&&t.shiftKey?e*this._optionsService.rawOptions.fastScrollSensitivity*this._optionsService.rawOptions.scrollSensitivity:e*this._optionsService.rawOptions.scrollSensitivity}handleTouchStart(e){this._lastTouchY=e.touches[0].pageY}handleTouchMove(e){const t=this._lastTouchY-e.touches[0].pageY;return this._lastTouchY=e.touches[0].pageY,0!==t&&(this._viewportElement.scrollTop+=t,this._bubbleScroll(e,t))}};t.Viewport=l=s([r(2,c.IBufferService),r(3,c.IOptionsService),r(4,o.ICharSizeService),r(5,o.IRenderService),r(6,o.ICoreBrowserService),r(7,o.IThemeService)],l)},3107:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.BufferDecorationRenderer=void 0;const n=i(3656),o=i(4725),a=i(844),h=i(2585);let c=t.BufferDecorationRenderer=class extends a.Disposable{constructor(e,t,i,s){super(),this._screenElement=e,this._bufferService=t,this._decorationService=i,this._renderService=s,this._decorationElements=new Map,this._altBufferIsActive=!1,this._dimensionsChanged=!1,this._container=document.createElement("div"),this._container.classList.add("xterm-decoration-container"),this._screenElement.appendChild(this._container),this.register(this._renderService.onRenderedViewportChange((()=>this._doRefreshDecorations()))),this.register(this._renderService.onDimensionsChange((()=>{this._dimensionsChanged=!0,this._queueRefresh()}))),this.register((0,n.addDisposableDomListener)(window,"resize",(()=>this._queueRefresh()))),this.register(this._bufferService.buffers.onBufferActivate((()=>{this._altBufferIsActive=this._bufferService.buffer===this._bufferService.buffers.alt}))),this.register(this._decorationService.onDecorationRegistered((()=>this._queueRefresh()))),this.register(this._decorationService.onDecorationRemoved((e=>this._removeDecoration(e)))),this.register((0,a.toDisposable)((()=>{this._container.remove(),this._decorationElements.clear()})))}_queueRefresh(){void 0===this._animationFrame&&(this._animationFrame=this._renderService.addRefreshCallback((()=>{this._doRefreshDecorations(),this._animationFrame=void 0})))}_doRefreshDecorations(){for(const e of this._decorationService.decorations)this._renderDecoration(e);this._dimensionsChanged=!1}_renderDecoration(e){this._refreshStyle(e),this._dimensionsChanged&&this._refreshXPosition(e)}_createElement(e){var t,i;const s=document.createElement("div");s.classList.add("xterm-decoration"),s.classList.toggle("xterm-decoration-top-layer","top"===(null===(t=null==e?void 0:e.options)||void 0===t?void 0:t.layer)),s.style.width=`${Math.round((e.options.width||1)*this._renderService.dimensions.css.cell.width)}px`,s.style.height=(e.options.height||1)*this._renderService.dimensions.css.cell.height+"px",s.style.top=(e.marker.line-this._bufferService.buffers.active.ydisp)*this._renderService.dimensions.css.cell.height+"px",s.style.lineHeight=`${this._renderService.dimensions.css.cell.height}px`;const r=null!==(i=e.options.x)&&void 0!==i?i:0;return r&&r>this._bufferService.cols&&(s.style.display="none"),this._refreshXPosition(e,s),s}_refreshStyle(e){const t=e.marker.line-this._bufferService.buffers.active.ydisp;if(t<0||t>=this._bufferService.rows)e.element&&(e.element.style.display="none",e.onRenderEmitter.fire(e.element));else{let i=this._decorationElements.get(e);i||(i=this._createElement(e),e.element=i,this._decorationElements.set(e,i),this._container.appendChild(i),e.onDispose((()=>{this._decorationElements.delete(e),i.remove()}))),i.style.top=t*this._renderService.dimensions.css.cell.height+"px",i.style.display=this._altBufferIsActive?"none":"block",e.onRenderEmitter.fire(i)}}_refreshXPosition(e,t=e.element){var i;if(!t)return;const s=null!==(i=e.options.x)&&void 0!==i?i:0;"right"===(e.options.anchor||"left")?t.style.right=s?s*this._renderService.dimensions.css.cell.width+"px":"":t.style.left=s?s*this._renderService.dimensions.css.cell.width+"px":""}_removeDecoration(e){var t;null===(t=this._decorationElements.get(e))||void 0===t||t.remove(),this._decorationElements.delete(e),e.dispose()}};t.BufferDecorationRenderer=c=s([r(1,h.IBufferService),r(2,h.IDecorationService),r(3,o.IRenderService)],c)},5871:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.ColorZoneStore=void 0,t.ColorZoneStore=class{constructor(){this._zones=[],this._zonePool=[],this._zonePoolIndex=0,this._linePadding={full:0,left:0,center:0,right:0}}get zones(){return this._zonePool.length=Math.min(this._zonePool.length,this._zones.length),this._zones}clear(){this._zones.length=0,this._zonePoolIndex=0}addDecoration(e){if(e.options.overviewRulerOptions){for(const t of this._zones)if(t.color===e.options.overviewRulerOptions.color&&t.position===e.options.overviewRulerOptions.position){if(this._lineIntersectsZone(t,e.marker.line))return;if(this._lineAdjacentToZone(t,e.marker.line,e.options.overviewRulerOptions.position))return void this._addLineToZone(t,e.marker.line)}if(this._zonePoolIndex=e.startBufferLine&&t<=e.endBufferLine}_lineAdjacentToZone(e,t,i){return t>=e.startBufferLine-this._linePadding[i||"full"]&&t<=e.endBufferLine+this._linePadding[i||"full"]}_addLineToZone(e,t){e.startBufferLine=Math.min(e.startBufferLine,t),e.endBufferLine=Math.max(e.endBufferLine,t)}}},5744:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.OverviewRulerRenderer=void 0;const n=i(5871),o=i(3656),a=i(4725),h=i(844),c=i(2585),l={full:0,left:0,center:0,right:0},d={full:0,left:0,center:0,right:0},_={full:0,left:0,center:0,right:0};let u=t.OverviewRulerRenderer=class extends h.Disposable{get _width(){return this._optionsService.options.overviewRulerWidth||0}constructor(e,t,i,s,r,o,a){var c;super(),this._viewportElement=e,this._screenElement=t,this._bufferService=i,this._decorationService=s,this._renderService=r,this._optionsService=o,this._coreBrowseService=a,this._colorZoneStore=new n.ColorZoneStore,this._shouldUpdateDimensions=!0,this._shouldUpdateAnchor=!0,this._lastKnownBufferLength=0,this._canvas=document.createElement("canvas"),this._canvas.classList.add("xterm-decoration-overview-ruler"),this._refreshCanvasDimensions(),null===(c=this._viewportElement.parentElement)||void 0===c||c.insertBefore(this._canvas,this._viewportElement);const l=this._canvas.getContext("2d");if(!l)throw new Error("Ctx cannot be null");this._ctx=l,this._registerDecorationListeners(),this._registerBufferChangeListeners(),this._registerDimensionChangeListeners(),this.register((0,h.toDisposable)((()=>{var e;null===(e=this._canvas)||void 0===e||e.remove()})))}_registerDecorationListeners(){this.register(this._decorationService.onDecorationRegistered((()=>this._queueRefresh(void 0,!0)))),this.register(this._decorationService.onDecorationRemoved((()=>this._queueRefresh(void 0,!0))))}_registerBufferChangeListeners(){this.register(this._renderService.onRenderedViewportChange((()=>this._queueRefresh()))),this.register(this._bufferService.buffers.onBufferActivate((()=>{this._canvas.style.display=this._bufferService.buffer===this._bufferService.buffers.alt?"none":"block"}))),this.register(this._bufferService.onScroll((()=>{this._lastKnownBufferLength!==this._bufferService.buffers.normal.lines.length&&(this._refreshDrawHeightConstants(),this._refreshColorZonePadding())})))}_registerDimensionChangeListeners(){this.register(this._renderService.onRender((()=>{this._containerHeight&&this._containerHeight===this._screenElement.clientHeight||(this._queueRefresh(!0),this._containerHeight=this._screenElement.clientHeight)}))),this.register(this._optionsService.onSpecificOptionChange("overviewRulerWidth",(()=>this._queueRefresh(!0)))),this.register((0,o.addDisposableDomListener)(this._coreBrowseService.window,"resize",(()=>this._queueRefresh(!0)))),this._queueRefresh(!0)}_refreshDrawConstants(){const e=Math.floor(this._canvas.width/3),t=Math.ceil(this._canvas.width/3);d.full=this._canvas.width,d.left=e,d.center=t,d.right=e,this._refreshDrawHeightConstants(),_.full=0,_.left=0,_.center=d.left,_.right=d.left+d.center}_refreshDrawHeightConstants(){l.full=Math.round(2*this._coreBrowseService.dpr);const e=this._canvas.height/this._bufferService.buffer.lines.length,t=Math.round(Math.max(Math.min(e,12),6)*this._coreBrowseService.dpr);l.left=t,l.center=t,l.right=t}_refreshColorZonePadding(){this._colorZoneStore.setPadding({full:Math.floor(this._bufferService.buffers.active.lines.length/(this._canvas.height-1)*l.full),left:Math.floor(this._bufferService.buffers.active.lines.length/(this._canvas.height-1)*l.left),center:Math.floor(this._bufferService.buffers.active.lines.length/(this._canvas.height-1)*l.center),right:Math.floor(this._bufferService.buffers.active.lines.length/(this._canvas.height-1)*l.right)}),this._lastKnownBufferLength=this._bufferService.buffers.normal.lines.length}_refreshCanvasDimensions(){this._canvas.style.width=`${this._width}px`,this._canvas.width=Math.round(this._width*this._coreBrowseService.dpr),this._canvas.style.height=`${this._screenElement.clientHeight}px`,this._canvas.height=Math.round(this._screenElement.clientHeight*this._coreBrowseService.dpr),this._refreshDrawConstants(),this._refreshColorZonePadding()}_refreshDecorations(){this._shouldUpdateDimensions&&this._refreshCanvasDimensions(),this._ctx.clearRect(0,0,this._canvas.width,this._canvas.height),this._colorZoneStore.clear();for(const e of this._decorationService.decorations)this._colorZoneStore.addDecoration(e);this._ctx.lineWidth=1;const e=this._colorZoneStore.zones;for(const t of e)"full"!==t.position&&this._renderColorZone(t);for(const t of e)"full"===t.position&&this._renderColorZone(t);this._shouldUpdateDimensions=!1,this._shouldUpdateAnchor=!1}_renderColorZone(e){this._ctx.fillStyle=e.color,this._ctx.fillRect(_[e.position||"full"],Math.round((this._canvas.height-1)*(e.startBufferLine/this._bufferService.buffers.active.lines.length)-l[e.position||"full"]/2),d[e.position||"full"],Math.round((this._canvas.height-1)*((e.endBufferLine-e.startBufferLine)/this._bufferService.buffers.active.lines.length)+l[e.position||"full"]))}_queueRefresh(e,t){this._shouldUpdateDimensions=e||this._shouldUpdateDimensions,this._shouldUpdateAnchor=t||this._shouldUpdateAnchor,void 0===this._animationFrame&&(this._animationFrame=this._coreBrowseService.window.requestAnimationFrame((()=>{this._refreshDecorations(),this._animationFrame=void 0})))}};t.OverviewRulerRenderer=u=s([r(2,c.IBufferService),r(3,c.IDecorationService),r(4,a.IRenderService),r(5,c.IOptionsService),r(6,a.ICoreBrowserService)],u)},2950:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.CompositionHelper=void 0;const n=i(4725),o=i(2585),a=i(2584);let h=t.CompositionHelper=class{get isComposing(){return this._isComposing}constructor(e,t,i,s,r,n){this._textarea=e,this._compositionView=t,this._bufferService=i,this._optionsService=s,this._coreService=r,this._renderService=n,this._isComposing=!1,this._isSendingComposition=!1,this._compositionPosition={start:0,end:0},this._dataAlreadySent=""}compositionstart(){this._isComposing=!0,this._compositionPosition.start=this._textarea.value.length,this._compositionView.textContent="",this._dataAlreadySent="",this._compositionView.classList.add("active")}compositionupdate(e){this._compositionView.textContent=e.data,this.updateCompositionElements(),setTimeout((()=>{this._compositionPosition.end=this._textarea.value.length}),0)}compositionend(){this._finalizeComposition(!0)}keydown(e){if(this._isComposing||this._isSendingComposition){if(229===e.keyCode)return!1;if(16===e.keyCode||17===e.keyCode||18===e.keyCode)return!1;this._finalizeComposition(!1)}return 229!==e.keyCode||(this._handleAnyTextareaChanges(),!1)}_finalizeComposition(e){if(this._compositionView.classList.remove("active"),this._isComposing=!1,e){const e={start:this._compositionPosition.start,end:this._compositionPosition.end};this._isSendingComposition=!0,setTimeout((()=>{if(this._isSendingComposition){let t;this._isSendingComposition=!1,e.start+=this._dataAlreadySent.length,t=this._isComposing?this._textarea.value.substring(e.start,e.end):this._textarea.value.substring(e.start),t.length>0&&this._coreService.triggerDataEvent(t,!0)}}),0)}else{this._isSendingComposition=!1;const e=this._textarea.value.substring(this._compositionPosition.start,this._compositionPosition.end);this._coreService.triggerDataEvent(e,!0)}}_handleAnyTextareaChanges(){const e=this._textarea.value;setTimeout((()=>{if(!this._isComposing){const t=this._textarea.value,i=t.replace(e,"");this._dataAlreadySent=i,t.length>e.length?this._coreService.triggerDataEvent(i,!0):t.lengththis.updateCompositionElements(!0)),0)}}};t.CompositionHelper=h=s([r(2,o.IBufferService),r(3,o.IOptionsService),r(4,o.ICoreService),r(5,n.IRenderService)],h)},9806:(e,t)=>{function i(e,t,i){const s=i.getBoundingClientRect(),r=e.getComputedStyle(i),n=parseInt(r.getPropertyValue("padding-left")),o=parseInt(r.getPropertyValue("padding-top"));return[t.clientX-s.left-n,t.clientY-s.top-o]}Object.defineProperty(t,"__esModule",{value:!0}),t.getCoords=t.getCoordsRelativeToElement=void 0,t.getCoordsRelativeToElement=i,t.getCoords=function(e,t,s,r,n,o,a,h,c){if(!o)return;const l=i(e,t,s);return l?(l[0]=Math.ceil((l[0]+(c?a/2:0))/a),l[1]=Math.ceil(l[1]/h),l[0]=Math.min(Math.max(l[0],1),r+(c?1:0)),l[1]=Math.min(Math.max(l[1],1),n),l):void 0}},9504:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.moveToCellSequence=void 0;const s=i(2584);function r(e,t,i,s){const r=e-n(e,i),a=t-n(t,i),l=Math.abs(r-a)-function(e,t,i){let s=0;const r=e-n(e,i),a=t-n(t,i);for(let n=0;n=0&&et?"A":"B"}function a(e,t,i,s,r,n){let o=e,a=t,h="";for(;o!==i||a!==s;)o+=r?1:-1,r&&o>n.cols-1?(h+=n.buffer.translateBufferLineToString(a,!1,e,o),o=0,e=0,a++):!r&&o<0&&(h+=n.buffer.translateBufferLineToString(a,!1,0,e+1),o=n.cols-1,e=o,a--);return h+n.buffer.translateBufferLineToString(a,!1,e,o)}function h(e,t){const i=t?"O":"[";return s.C0.ESC+i+e}function c(e,t){e=Math.floor(e);let i="";for(let s=0;s0?s-n(s,o):t;const _=s,u=function(e,t,i,s,o,a){let h;return h=r(i,s,o,a).length>0?s-n(s,o):t,e=i&&he?"D":"C",c(Math.abs(o-e),h(d,s));d=l>t?"D":"C";const _=Math.abs(l-t);return c(function(e,t){return t.cols-e}(l>t?e:o,i)+(_-1)*i.cols+1+((l>t?o:e)-1),h(d,s))}},1296:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.DomRenderer=void 0;const n=i(3787),o=i(2550),a=i(2223),h=i(6171),c=i(4725),l=i(8055),d=i(8460),_=i(844),u=i(2585),f="xterm-dom-renderer-owner-",v="xterm-rows",p="xterm-fg-",g="xterm-bg-",m="xterm-focus",S="xterm-selection";let C=1,b=t.DomRenderer=class extends _.Disposable{constructor(e,t,i,s,r,a,c,l,u,p){super(),this._element=e,this._screenElement=t,this._viewportElement=i,this._linkifier2=s,this._charSizeService=a,this._optionsService=c,this._bufferService=l,this._coreBrowserService=u,this._themeService=p,this._terminalClass=C++,this._rowElements=[],this.onRequestRedraw=this.register(new d.EventEmitter).event,this._rowContainer=document.createElement("div"),this._rowContainer.classList.add(v),this._rowContainer.style.lineHeight="normal",this._rowContainer.setAttribute("aria-hidden","true"),this._refreshRowElements(this._bufferService.cols,this._bufferService.rows),this._selectionContainer=document.createElement("div"),this._selectionContainer.classList.add(S),this._selectionContainer.setAttribute("aria-hidden","true"),this.dimensions=(0,h.createRenderDimensions)(),this._updateDimensions(),this.register(this._optionsService.onOptionChange((()=>this._handleOptionsChanged()))),this.register(this._themeService.onChangeColors((e=>this._injectCss(e)))),this._injectCss(this._themeService.colors),this._rowFactory=r.createInstance(n.DomRendererRowFactory,document),this._element.classList.add(f+this._terminalClass),this._screenElement.appendChild(this._rowContainer),this._screenElement.appendChild(this._selectionContainer),this.register(this._linkifier2.onShowLinkUnderline((e=>this._handleLinkHover(e)))),this.register(this._linkifier2.onHideLinkUnderline((e=>this._handleLinkLeave(e)))),this.register((0,_.toDisposable)((()=>{this._element.classList.remove(f+this._terminalClass),this._rowContainer.remove(),this._selectionContainer.remove(),this._widthCache.dispose(),this._themeStyleElement.remove(),this._dimensionsStyleElement.remove()}))),this._widthCache=new o.WidthCache(document),this._widthCache.setFont(this._optionsService.rawOptions.fontFamily,this._optionsService.rawOptions.fontSize,this._optionsService.rawOptions.fontWeight,this._optionsService.rawOptions.fontWeightBold),this._setDefaultSpacing()}_updateDimensions(){const e=this._coreBrowserService.dpr;this.dimensions.device.char.width=this._charSizeService.width*e,this.dimensions.device.char.height=Math.ceil(this._charSizeService.height*e),this.dimensions.device.cell.width=this.dimensions.device.char.width+Math.round(this._optionsService.rawOptions.letterSpacing),this.dimensions.device.cell.height=Math.floor(this.dimensions.device.char.height*this._optionsService.rawOptions.lineHeight),this.dimensions.device.char.left=0,this.dimensions.device.char.top=0,this.dimensions.device.canvas.width=this.dimensions.device.cell.width*this._bufferService.cols,this.dimensions.device.canvas.height=this.dimensions.device.cell.height*this._bufferService.rows,this.dimensions.css.canvas.width=Math.round(this.dimensions.device.canvas.width/e),this.dimensions.css.canvas.height=Math.round(this.dimensions.device.canvas.height/e),this.dimensions.css.cell.width=this.dimensions.css.canvas.width/this._bufferService.cols,this.dimensions.css.cell.height=this.dimensions.css.canvas.height/this._bufferService.rows;for(const e of this._rowElements)e.style.width=`${this.dimensions.css.canvas.width}px`,e.style.height=`${this.dimensions.css.cell.height}px`,e.style.lineHeight=`${this.dimensions.css.cell.height}px`,e.style.overflow="hidden";this._dimensionsStyleElement||(this._dimensionsStyleElement=document.createElement("style"),this._screenElement.appendChild(this._dimensionsStyleElement));const t=`${this._terminalSelector} .${v} span { display: inline-block; height: 100%; vertical-align: top;}`;this._dimensionsStyleElement.textContent=t,this._selectionContainer.style.height=this._viewportElement.style.height,this._screenElement.style.width=`${this.dimensions.css.canvas.width}px`,this._screenElement.style.height=`${this.dimensions.css.canvas.height}px`}_injectCss(e){this._themeStyleElement||(this._themeStyleElement=document.createElement("style"),this._screenElement.appendChild(this._themeStyleElement));let t=`${this._terminalSelector} .${v} { color: ${e.foreground.css}; font-family: ${this._optionsService.rawOptions.fontFamily}; font-size: ${this._optionsService.rawOptions.fontSize}px; font-kerning: none; white-space: pre}`;t+=`${this._terminalSelector} .${v} .xterm-dim { color: ${l.color.multiplyOpacity(e.foreground,.5).css};}`,t+=`${this._terminalSelector} span:not(.xterm-bold) { font-weight: ${this._optionsService.rawOptions.fontWeight};}${this._terminalSelector} span.xterm-bold { font-weight: ${this._optionsService.rawOptions.fontWeightBold};}${this._terminalSelector} span.xterm-italic { font-style: italic;}`,t+="@keyframes blink_box_shadow_"+this._terminalClass+" { 50% { border-bottom-style: hidden; }}",t+="@keyframes blink_block_"+this._terminalClass+" { 0% {"+` background-color: ${e.cursor.css};`+` color: ${e.cursorAccent.css}; } 50% { background-color: inherit;`+` color: ${e.cursor.css}; }}`,t+=`${this._terminalSelector} .${v}.${m} .xterm-cursor.xterm-cursor-blink:not(.xterm-cursor-block) { animation: blink_box_shadow_`+this._terminalClass+" 1s step-end infinite;}"+`${this._terminalSelector} .${v}.${m} .xterm-cursor.xterm-cursor-blink.xterm-cursor-block { animation: blink_block_`+this._terminalClass+" 1s step-end infinite;}"+`${this._terminalSelector} .${v} .xterm-cursor.xterm-cursor-block {`+` background-color: ${e.cursor.css};`+` color: ${e.cursorAccent.css};}`+`${this._terminalSelector} .${v} .xterm-cursor.xterm-cursor-outline {`+` outline: 1px solid ${e.cursor.css}; outline-offset: -1px;}`+`${this._terminalSelector} .${v} .xterm-cursor.xterm-cursor-bar {`+` box-shadow: ${this._optionsService.rawOptions.cursorWidth}px 0 0 ${e.cursor.css} inset;}`+`${this._terminalSelector} .${v} .xterm-cursor.xterm-cursor-underline {`+` border-bottom: 1px ${e.cursor.css}; border-bottom-style: solid; height: calc(100% - 1px);}`,t+=`${this._terminalSelector} .${S} { position: absolute; top: 0; left: 0; z-index: 1; pointer-events: none;}${this._terminalSelector}.focus .${S} div { position: absolute; background-color: ${e.selectionBackgroundOpaque.css};}${this._terminalSelector} .${S} div { position: absolute; background-color: ${e.selectionInactiveBackgroundOpaque.css};}`;for(const[i,s]of e.ansi.entries())t+=`${this._terminalSelector} .${p}${i} { color: ${s.css}; }${this._terminalSelector} .${p}${i}.xterm-dim { color: ${l.color.multiplyOpacity(s,.5).css}; }${this._terminalSelector} .${g}${i} { background-color: ${s.css}; }`;t+=`${this._terminalSelector} .${p}${a.INVERTED_DEFAULT_COLOR} { color: ${l.color.opaque(e.background).css}; }${this._terminalSelector} .${p}${a.INVERTED_DEFAULT_COLOR}.xterm-dim { color: ${l.color.multiplyOpacity(l.color.opaque(e.background),.5).css}; }${this._terminalSelector} .${g}${a.INVERTED_DEFAULT_COLOR} { background-color: ${e.foreground.css}; }`,this._themeStyleElement.textContent=t}_setDefaultSpacing(){const e=this.dimensions.css.cell.width-this._widthCache.get("W",!1,!1);this._rowContainer.style.letterSpacing=`${e}px`,this._rowFactory.defaultSpacing=e}handleDevicePixelRatioChange(){this._updateDimensions(),this._widthCache.clear(),this._setDefaultSpacing()}_refreshRowElements(e,t){for(let e=this._rowElements.length;e<=t;e++){const e=document.createElement("div");this._rowContainer.appendChild(e),this._rowElements.push(e)}for(;this._rowElements.length>t;)this._rowContainer.removeChild(this._rowElements.pop())}handleResize(e,t){this._refreshRowElements(e,t),this._updateDimensions()}handleCharSizeChanged(){this._updateDimensions(),this._widthCache.clear(),this._setDefaultSpacing()}handleBlur(){this._rowContainer.classList.remove(m)}handleFocus(){this._rowContainer.classList.add(m),this.renderRows(this._bufferService.buffer.y,this._bufferService.buffer.y)}handleSelectionChanged(e,t,i){if(this._selectionContainer.replaceChildren(),this._rowFactory.handleSelectionChanged(e,t,i),this.renderRows(0,this._bufferService.rows-1),!e||!t)return;const s=e[1]-this._bufferService.buffer.ydisp,r=t[1]-this._bufferService.buffer.ydisp,n=Math.max(s,0),o=Math.min(r,this._bufferService.rows-1);if(n>=this._bufferService.rows||o<0)return;const a=document.createDocumentFragment();if(i){const i=e[0]>t[0];a.appendChild(this._createSelectionElement(n,i?t[0]:e[0],i?e[0]:t[0],o-n+1))}else{const i=s===n?e[0]:0,h=n===r?t[0]:this._bufferService.cols;a.appendChild(this._createSelectionElement(n,i,h));const c=o-n-1;if(a.appendChild(this._createSelectionElement(n+1,0,this._bufferService.cols,c)),n!==o){const e=r===o?t[0]:this._bufferService.cols;a.appendChild(this._createSelectionElement(o,0,e))}}this._selectionContainer.appendChild(a)}_createSelectionElement(e,t,i,s=1){const r=document.createElement("div");return r.style.height=s*this.dimensions.css.cell.height+"px",r.style.top=e*this.dimensions.css.cell.height+"px",r.style.left=t*this.dimensions.css.cell.width+"px",r.style.width=this.dimensions.css.cell.width*(i-t)+"px",r}handleCursorMove(){}_handleOptionsChanged(){this._updateDimensions(),this._injectCss(this._themeService.colors),this._widthCache.setFont(this._optionsService.rawOptions.fontFamily,this._optionsService.rawOptions.fontSize,this._optionsService.rawOptions.fontWeight,this._optionsService.rawOptions.fontWeightBold),this._setDefaultSpacing()}clear(){for(const e of this._rowElements)e.replaceChildren()}renderRows(e,t){const i=this._bufferService.buffer,s=i.ybase+i.y,r=Math.min(i.x,this._bufferService.cols-1),n=this._optionsService.rawOptions.cursorBlink,o=this._optionsService.rawOptions.cursorStyle,a=this._optionsService.rawOptions.cursorInactiveStyle;for(let h=e;h<=t;h++){const e=h+i.ydisp,t=this._rowElements[h],c=i.lines.get(e);if(!t||!c)break;t.replaceChildren(...this._rowFactory.createRow(c,e,e===s,o,a,r,n,this.dimensions.css.cell.width,this._widthCache,-1,-1))}}get _terminalSelector(){return`.${f}${this._terminalClass}`}_handleLinkHover(e){this._setCellUnderline(e.x1,e.x2,e.y1,e.y2,e.cols,!0)}_handleLinkLeave(e){this._setCellUnderline(e.x1,e.x2,e.y1,e.y2,e.cols,!1)}_setCellUnderline(e,t,i,s,r,n){i<0&&(e=0),s<0&&(t=0);const o=this._bufferService.rows-1;i=Math.max(Math.min(i,o),0),s=Math.max(Math.min(s,o),0),r=Math.min(r,this._bufferService.cols);const a=this._bufferService.buffer,h=a.ybase+a.y,c=Math.min(a.x,r-1),l=this._optionsService.rawOptions.cursorBlink,d=this._optionsService.rawOptions.cursorStyle,_=this._optionsService.rawOptions.cursorInactiveStyle;for(let o=i;o<=s;++o){const u=o+a.ydisp,f=this._rowElements[o],v=a.lines.get(u);if(!f||!v)break;f.replaceChildren(...this._rowFactory.createRow(v,u,u===h,d,_,c,l,this.dimensions.css.cell.width,this._widthCache,n?o===i?e:0:-1,n?(o===s?t:r)-1:-1))}}};t.DomRenderer=b=s([r(4,u.IInstantiationService),r(5,c.ICharSizeService),r(6,u.IOptionsService),r(7,u.IBufferService),r(8,c.ICoreBrowserService),r(9,c.IThemeService)],b)},3787:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.DomRendererRowFactory=void 0;const n=i(2223),o=i(643),a=i(511),h=i(2585),c=i(8055),l=i(4725),d=i(4269),_=i(6171),u=i(3734);let f=t.DomRendererRowFactory=class{constructor(e,t,i,s,r,n,o){this._document=e,this._characterJoinerService=t,this._optionsService=i,this._coreBrowserService=s,this._coreService=r,this._decorationService=n,this._themeService=o,this._workCell=new a.CellData,this._columnSelectMode=!1,this.defaultSpacing=0}handleSelectionChanged(e,t,i){this._selectionStart=e,this._selectionEnd=t,this._columnSelectMode=i}createRow(e,t,i,s,r,a,h,l,_,f,p){const g=[],m=this._characterJoinerService.getJoinedCharacters(t),S=this._themeService.colors;let C,b=e.getNoBgTrimmedLength();i&&b0&&M===m[0][0]){O=!0;const t=m.shift();I=new d.JoinedCellData(this._workCell,e.translateToString(!0,t[0],t[1]),t[1]-t[0]),P=t[1]-1,b=I.getWidth()}const H=this._isCellInSelection(M,t),F=i&&M===a,W=T&&M>=f&&M<=p;let U=!1;this._decorationService.forEachDecorationAtCell(M,t,void 0,(e=>{U=!0}));let N=I.getChars()||o.WHITESPACE_CELL_CHAR;if(" "===N&&(I.isUnderline()||I.isOverline())&&(N=" "),A=b*l-_.get(N,I.isBold(),I.isItalic()),C){if(y&&(H&&x||!H&&!x&&I.bg===E)&&(H&&x&&S.selectionForeground||I.fg===k)&&I.extended.ext===L&&W===D&&A===R&&!F&&!O&&!U){w+=N,y++;continue}y&&(C.textContent=w),C=this._document.createElement("span"),y=0,w=""}else C=this._document.createElement("span");if(E=I.bg,k=I.fg,L=I.extended.ext,D=W,R=A,x=H,O&&a>=M&&a<=P&&(a=M),!this._coreService.isCursorHidden&&F)if(B.push("xterm-cursor"),this._coreBrowserService.isFocused)h&&B.push("xterm-cursor-blink"),B.push("bar"===s?"xterm-cursor-bar":"underline"===s?"xterm-cursor-underline":"xterm-cursor-block");else if(r)switch(r){case"outline":B.push("xterm-cursor-outline");break;case"block":B.push("xterm-cursor-block");break;case"bar":B.push("xterm-cursor-bar");break;case"underline":B.push("xterm-cursor-underline")}if(I.isBold()&&B.push("xterm-bold"),I.isItalic()&&B.push("xterm-italic"),I.isDim()&&B.push("xterm-dim"),w=I.isInvisible()?o.WHITESPACE_CELL_CHAR:I.getChars()||o.WHITESPACE_CELL_CHAR,I.isUnderline()&&(B.push(`xterm-underline-${I.extended.underlineStyle}`)," "===w&&(w=" "),!I.isUnderlineColorDefault()))if(I.isUnderlineColorRGB())C.style.textDecorationColor=`rgb(${u.AttributeData.toColorRGB(I.getUnderlineColor()).join(",")})`;else{let e=I.getUnderlineColor();this._optionsService.rawOptions.drawBoldTextInBrightColors&&I.isBold()&&e<8&&(e+=8),C.style.textDecorationColor=S.ansi[e].css}I.isOverline()&&(B.push("xterm-overline")," "===w&&(w=" ")),I.isStrikethrough()&&B.push("xterm-strikethrough"),W&&(C.style.textDecoration="underline");let $=I.getFgColor(),j=I.getFgColorMode(),z=I.getBgColor(),K=I.getBgColorMode();const q=!!I.isInverse();if(q){const e=$;$=z,z=e;const t=j;j=K,K=t}let V,G,X,J=!1;switch(this._decorationService.forEachDecorationAtCell(M,t,void 0,(e=>{"top"!==e.options.layer&&J||(e.backgroundColorRGB&&(K=50331648,z=e.backgroundColorRGB.rgba>>8&16777215,V=e.backgroundColorRGB),e.foregroundColorRGB&&(j=50331648,$=e.foregroundColorRGB.rgba>>8&16777215,G=e.foregroundColorRGB),J="top"===e.options.layer)})),!J&&H&&(V=this._coreBrowserService.isFocused?S.selectionBackgroundOpaque:S.selectionInactiveBackgroundOpaque,z=V.rgba>>8&16777215,K=50331648,J=!0,S.selectionForeground&&(j=50331648,$=S.selectionForeground.rgba>>8&16777215,G=S.selectionForeground)),J&&B.push("xterm-decoration-top"),K){case 16777216:case 33554432:X=S.ansi[z],B.push(`xterm-bg-${z}`);break;case 50331648:X=c.rgba.toColor(z>>16,z>>8&255,255&z),this._addStyle(C,`background-color:#${v((z>>>0).toString(16),"0",6)}`);break;default:q?(X=S.foreground,B.push(`xterm-bg-${n.INVERTED_DEFAULT_COLOR}`)):X=S.background}switch(V||I.isDim()&&(V=c.color.multiplyOpacity(X,.5)),j){case 16777216:case 33554432:I.isBold()&&$<8&&this._optionsService.rawOptions.drawBoldTextInBrightColors&&($+=8),this._applyMinimumContrast(C,X,S.ansi[$],I,V,void 0)||B.push(`xterm-fg-${$}`);break;case 50331648:const e=c.rgba.toColor($>>16&255,$>>8&255,255&$);this._applyMinimumContrast(C,X,e,I,V,G)||this._addStyle(C,`color:#${v($.toString(16),"0",6)}`);break;default:this._applyMinimumContrast(C,X,S.foreground,I,V,void 0)||q&&B.push(`xterm-fg-${n.INVERTED_DEFAULT_COLOR}`)}B.length&&(C.className=B.join(" "),B.length=0),F||O||U?C.textContent=w:y++,A!==this.defaultSpacing&&(C.style.letterSpacing=`${A}px`),g.push(C),M=P}return C&&y&&(C.textContent=w),g}_applyMinimumContrast(e,t,i,s,r,n){if(1===this._optionsService.rawOptions.minimumContrastRatio||(0,_.excludeFromContrastRatioDemands)(s.getCode()))return!1;const o=this._getContrastCache(s);let a;if(r||n||(a=o.getColor(t.rgba,i.rgba)),void 0===a){const e=this._optionsService.rawOptions.minimumContrastRatio/(s.isDim()?2:1);a=c.color.ensureContrastRatio(r||t,n||i,e),o.setColor((r||t).rgba,(n||i).rgba,null!=a?a:null)}return!!a&&(this._addStyle(e,`color:${a.css}`),!0)}_getContrastCache(e){return e.isDim()?this._themeService.colors.halfContrastCache:this._themeService.colors.contrastCache}_addStyle(e,t){e.setAttribute("style",`${e.getAttribute("style")||""}${t};`)}_isCellInSelection(e,t){const i=this._selectionStart,s=this._selectionEnd;return!(!i||!s)&&(this._columnSelectMode?i[0]<=s[0]?e>=i[0]&&t>=i[1]&&e=i[1]&&e>=s[0]&&t<=s[1]:t>i[1]&&t=i[0]&&e=i[0])}};function v(e,t,i){for(;e.length{Object.defineProperty(t,"__esModule",{value:!0}),t.WidthCache=void 0,t.WidthCache=class{constructor(e){this._flat=new Float32Array(256),this._font="",this._fontSize=0,this._weight="normal",this._weightBold="bold",this._measureElements=[],this._container=e.createElement("div"),this._container.style.position="absolute",this._container.style.top="-50000px",this._container.style.width="50000px",this._container.style.whiteSpace="pre",this._container.style.fontKerning="none";const t=e.createElement("span"),i=e.createElement("span");i.style.fontWeight="bold";const s=e.createElement("span");s.style.fontStyle="italic";const r=e.createElement("span");r.style.fontWeight="bold",r.style.fontStyle="italic",this._measureElements=[t,i,s,r],this._container.appendChild(t),this._container.appendChild(i),this._container.appendChild(s),this._container.appendChild(r),e.body.appendChild(this._container),this.clear()}dispose(){this._container.remove(),this._measureElements.length=0,this._holey=void 0}clear(){this._flat.fill(-9999),this._holey=new Map}setFont(e,t,i,s){e===this._font&&t===this._fontSize&&i===this._weight&&s===this._weightBold||(this._font=e,this._fontSize=t,this._weight=i,this._weightBold=s,this._container.style.fontFamily=this._font,this._container.style.fontSize=`${this._fontSize}px`,this._measureElements[0].style.fontWeight=`${i}`,this._measureElements[1].style.fontWeight=`${s}`,this._measureElements[2].style.fontWeight=`${i}`,this._measureElements[3].style.fontWeight=`${s}`,this.clear())}get(e,t,i){let s=0;if(!t&&!i&&1===e.length&&(s=e.charCodeAt(0))<256)return-9999!==this._flat[s]?this._flat[s]:this._flat[s]=this._measure(e,0);let r=e;t&&(r+="B"),i&&(r+="I");let n=this._holey.get(r);if(void 0===n){let s=0;t&&(s|=1),i&&(s|=2),n=this._measure(e,s),this._holey.set(r,n)}return n}_measure(e,t){const i=this._measureElements[t];return i.textContent=e.repeat(32),i.offsetWidth/32}}},2223:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.TEXT_BASELINE=t.DIM_OPACITY=t.INVERTED_DEFAULT_COLOR=void 0;const s=i(6114);t.INVERTED_DEFAULT_COLOR=257,t.DIM_OPACITY=.5,t.TEXT_BASELINE=s.isFirefox||s.isLegacyEdge?"bottom":"ideographic"},6171:(e,t)=>{function i(e){return 57508<=e&&e<=57558}Object.defineProperty(t,"__esModule",{value:!0}),t.createRenderDimensions=t.excludeFromContrastRatioDemands=t.isRestrictedPowerlineGlyph=t.isPowerlineGlyph=t.throwIfFalsy=void 0,t.throwIfFalsy=function(e){if(!e)throw new Error("value must not be falsy");return e},t.isPowerlineGlyph=i,t.isRestrictedPowerlineGlyph=function(e){return 57520<=e&&e<=57527},t.excludeFromContrastRatioDemands=function(e){return i(e)||function(e){return 9472<=e&&e<=9631}(e)},t.createRenderDimensions=function(){return{css:{canvas:{width:0,height:0},cell:{width:0,height:0}},device:{canvas:{width:0,height:0},cell:{width:0,height:0},char:{width:0,height:0,left:0,top:0}}}}},456:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.SelectionModel=void 0,t.SelectionModel=class{constructor(e){this._bufferService=e,this.isSelectAllActive=!1,this.selectionStartLength=0}clearSelection(){this.selectionStart=void 0,this.selectionEnd=void 0,this.isSelectAllActive=!1,this.selectionStartLength=0}get finalSelectionStart(){return this.isSelectAllActive?[0,0]:this.selectionEnd&&this.selectionStart&&this.areSelectionValuesReversed()?this.selectionEnd:this.selectionStart}get finalSelectionEnd(){if(this.isSelectAllActive)return[this._bufferService.cols,this._bufferService.buffer.ybase+this._bufferService.rows-1];if(this.selectionStart){if(!this.selectionEnd||this.areSelectionValuesReversed()){const e=this.selectionStart[0]+this.selectionStartLength;return e>this._bufferService.cols?e%this._bufferService.cols==0?[this._bufferService.cols,this.selectionStart[1]+Math.floor(e/this._bufferService.cols)-1]:[e%this._bufferService.cols,this.selectionStart[1]+Math.floor(e/this._bufferService.cols)]:[e,this.selectionStart[1]]}if(this.selectionStartLength&&this.selectionEnd[1]===this.selectionStart[1]){const e=this.selectionStart[0]+this.selectionStartLength;return e>this._bufferService.cols?[e%this._bufferService.cols,this.selectionStart[1]+Math.floor(e/this._bufferService.cols)]:[Math.max(e,this.selectionEnd[0]),this.selectionEnd[1]]}return this.selectionEnd}}areSelectionValuesReversed(){const e=this.selectionStart,t=this.selectionEnd;return!(!e||!t)&&(e[1]>t[1]||e[1]===t[1]&&e[0]>t[0])}handleTrim(e){return this.selectionStart&&(this.selectionStart[1]-=e),this.selectionEnd&&(this.selectionEnd[1]-=e),this.selectionEnd&&this.selectionEnd[1]<0?(this.clearSelection(),!0):(this.selectionStart&&this.selectionStart[1]<0&&(this.selectionStart[1]=0),!1)}}},428:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.CharSizeService=void 0;const n=i(2585),o=i(8460),a=i(844);let h=t.CharSizeService=class extends a.Disposable{get hasValidSize(){return this.width>0&&this.height>0}constructor(e,t,i){super(),this._optionsService=i,this.width=0,this.height=0,this._onCharSizeChange=this.register(new o.EventEmitter),this.onCharSizeChange=this._onCharSizeChange.event,this._measureStrategy=new c(e,t,this._optionsService),this.register(this._optionsService.onMultipleOptionChange(["fontFamily","fontSize"],(()=>this.measure())))}measure(){const e=this._measureStrategy.measure();e.width===this.width&&e.height===this.height||(this.width=e.width,this.height=e.height,this._onCharSizeChange.fire())}};t.CharSizeService=h=s([r(2,n.IOptionsService)],h);class c{constructor(e,t,i){this._document=e,this._parentElement=t,this._optionsService=i,this._result={width:0,height:0},this._measureElement=this._document.createElement("span"),this._measureElement.classList.add("xterm-char-measure-element"),this._measureElement.textContent="W".repeat(32),this._measureElement.setAttribute("aria-hidden","true"),this._measureElement.style.whiteSpace="pre",this._measureElement.style.fontKerning="none",this._parentElement.appendChild(this._measureElement)}measure(){this._measureElement.style.fontFamily=this._optionsService.rawOptions.fontFamily,this._measureElement.style.fontSize=`${this._optionsService.rawOptions.fontSize}px`;const e={height:Number(this._measureElement.offsetHeight),width:Number(this._measureElement.offsetWidth)};return 0!==e.width&&0!==e.height&&(this._result.width=e.width/32,this._result.height=Math.ceil(e.height)),this._result}}},4269:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.CharacterJoinerService=t.JoinedCellData=void 0;const n=i(3734),o=i(643),a=i(511),h=i(2585);class c extends n.AttributeData{constructor(e,t,i){super(),this.content=0,this.combinedData="",this.fg=e.fg,this.bg=e.bg,this.combinedData=t,this._width=i}isCombined(){return 2097152}getWidth(){return this._width}getChars(){return this.combinedData}getCode(){return 2097151}setFromCharData(e){throw new Error("not implemented")}getAsCharData(){return[this.fg,this.getChars(),this.getWidth(),this.getCode()]}}t.JoinedCellData=c;let l=t.CharacterJoinerService=class e{constructor(e){this._bufferService=e,this._characterJoiners=[],this._nextCharacterJoinerId=0,this._workCell=new a.CellData}register(e){const t={id:this._nextCharacterJoinerId++,handler:e};return this._characterJoiners.push(t),t.id}deregister(e){for(let t=0;t1){const e=this._getJoinedRanges(s,a,n,t,r);for(let t=0;t1){const e=this._getJoinedRanges(s,a,n,t,r);for(let t=0;t{Object.defineProperty(t,"__esModule",{value:!0}),t.CoreBrowserService=void 0,t.CoreBrowserService=class{constructor(e,t){this._textarea=e,this.window=t,this._isFocused=!1,this._cachedIsFocused=void 0,this._textarea.addEventListener("focus",(()=>this._isFocused=!0)),this._textarea.addEventListener("blur",(()=>this._isFocused=!1))}get dpr(){return this.window.devicePixelRatio}get isFocused(){return void 0===this._cachedIsFocused&&(this._cachedIsFocused=this._isFocused&&this._textarea.ownerDocument.hasFocus(),queueMicrotask((()=>this._cachedIsFocused=void 0))),this._cachedIsFocused}}},8934:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.MouseService=void 0;const n=i(4725),o=i(9806);let a=t.MouseService=class{constructor(e,t){this._renderService=e,this._charSizeService=t}getCoords(e,t,i,s,r){return(0,o.getCoords)(window,e,t,i,s,this._charSizeService.hasValidSize,this._renderService.dimensions.css.cell.width,this._renderService.dimensions.css.cell.height,r)}getMouseReportCoords(e,t){const i=(0,o.getCoordsRelativeToElement)(window,e,t);if(this._charSizeService.hasValidSize)return i[0]=Math.min(Math.max(i[0],0),this._renderService.dimensions.css.canvas.width-1),i[1]=Math.min(Math.max(i[1],0),this._renderService.dimensions.css.canvas.height-1),{col:Math.floor(i[0]/this._renderService.dimensions.css.cell.width),row:Math.floor(i[1]/this._renderService.dimensions.css.cell.height),x:Math.floor(i[0]),y:Math.floor(i[1])}}};t.MouseService=a=s([r(0,n.IRenderService),r(1,n.ICharSizeService)],a)},3230:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.RenderService=void 0;const n=i(3656),o=i(6193),a=i(5596),h=i(4725),c=i(8460),l=i(844),d=i(7226),_=i(2585);let u=t.RenderService=class extends l.Disposable{get dimensions(){return this._renderer.value.dimensions}constructor(e,t,i,s,r,h,_,u){if(super(),this._rowCount=e,this._charSizeService=s,this._renderer=this.register(new l.MutableDisposable),this._pausedResizeTask=new d.DebouncedIdleTask,this._isPaused=!1,this._needsFullRefresh=!1,this._isNextRenderRedrawOnly=!0,this._needsSelectionRefresh=!1,this._canvasWidth=0,this._canvasHeight=0,this._selectionState={start:void 0,end:void 0,columnSelectMode:!1},this._onDimensionsChange=this.register(new c.EventEmitter),this.onDimensionsChange=this._onDimensionsChange.event,this._onRenderedViewportChange=this.register(new c.EventEmitter),this.onRenderedViewportChange=this._onRenderedViewportChange.event,this._onRender=this.register(new c.EventEmitter),this.onRender=this._onRender.event,this._onRefreshRequest=this.register(new c.EventEmitter),this.onRefreshRequest=this._onRefreshRequest.event,this._renderDebouncer=new o.RenderDebouncer(_.window,((e,t)=>this._renderRows(e,t))),this.register(this._renderDebouncer),this._screenDprMonitor=new a.ScreenDprMonitor(_.window),this._screenDprMonitor.setListener((()=>this.handleDevicePixelRatioChange())),this.register(this._screenDprMonitor),this.register(h.onResize((()=>this._fullRefresh()))),this.register(h.buffers.onBufferActivate((()=>{var e;return null===(e=this._renderer.value)||void 0===e?void 0:e.clear()}))),this.register(i.onOptionChange((()=>this._handleOptionsChanged()))),this.register(this._charSizeService.onCharSizeChange((()=>this.handleCharSizeChanged()))),this.register(r.onDecorationRegistered((()=>this._fullRefresh()))),this.register(r.onDecorationRemoved((()=>this._fullRefresh()))),this.register(i.onMultipleOptionChange(["customGlyphs","drawBoldTextInBrightColors","letterSpacing","lineHeight","fontFamily","fontSize","fontWeight","fontWeightBold","minimumContrastRatio"],(()=>{this.clear(),this.handleResize(h.cols,h.rows),this._fullRefresh()}))),this.register(i.onMultipleOptionChange(["cursorBlink","cursorStyle"],(()=>this.refreshRows(h.buffer.y,h.buffer.y,!0)))),this.register((0,n.addDisposableDomListener)(_.window,"resize",(()=>this.handleDevicePixelRatioChange()))),this.register(u.onChangeColors((()=>this._fullRefresh()))),"IntersectionObserver"in _.window){const e=new _.window.IntersectionObserver((e=>this._handleIntersectionChange(e[e.length-1])),{threshold:0});e.observe(t),this.register({dispose:()=>e.disconnect()})}}_handleIntersectionChange(e){this._isPaused=void 0===e.isIntersecting?0===e.intersectionRatio:!e.isIntersecting,this._isPaused||this._charSizeService.hasValidSize||this._charSizeService.measure(),!this._isPaused&&this._needsFullRefresh&&(this._pausedResizeTask.flush(),this.refreshRows(0,this._rowCount-1),this._needsFullRefresh=!1)}refreshRows(e,t,i=!1){this._isPaused?this._needsFullRefresh=!0:(i||(this._isNextRenderRedrawOnly=!1),this._renderDebouncer.refresh(e,t,this._rowCount))}_renderRows(e,t){this._renderer.value&&(e=Math.min(e,this._rowCount-1),t=Math.min(t,this._rowCount-1),this._renderer.value.renderRows(e,t),this._needsSelectionRefresh&&(this._renderer.value.handleSelectionChanged(this._selectionState.start,this._selectionState.end,this._selectionState.columnSelectMode),this._needsSelectionRefresh=!1),this._isNextRenderRedrawOnly||this._onRenderedViewportChange.fire({start:e,end:t}),this._onRender.fire({start:e,end:t}),this._isNextRenderRedrawOnly=!0)}resize(e,t){this._rowCount=t,this._fireOnCanvasResize()}_handleOptionsChanged(){this._renderer.value&&(this.refreshRows(0,this._rowCount-1),this._fireOnCanvasResize())}_fireOnCanvasResize(){this._renderer.value&&(this._renderer.value.dimensions.css.canvas.width===this._canvasWidth&&this._renderer.value.dimensions.css.canvas.height===this._canvasHeight||this._onDimensionsChange.fire(this._renderer.value.dimensions))}hasRenderer(){return!!this._renderer.value}setRenderer(e){this._renderer.value=e,this._renderer.value.onRequestRedraw((e=>this.refreshRows(e.start,e.end,!0))),this._needsSelectionRefresh=!0,this._fullRefresh()}addRefreshCallback(e){return this._renderDebouncer.addRefreshCallback(e)}_fullRefresh(){this._isPaused?this._needsFullRefresh=!0:this.refreshRows(0,this._rowCount-1)}clearTextureAtlas(){var e,t;this._renderer.value&&(null===(t=(e=this._renderer.value).clearTextureAtlas)||void 0===t||t.call(e),this._fullRefresh())}handleDevicePixelRatioChange(){this._charSizeService.measure(),this._renderer.value&&(this._renderer.value.handleDevicePixelRatioChange(),this.refreshRows(0,this._rowCount-1))}handleResize(e,t){this._renderer.value&&(this._isPaused?this._pausedResizeTask.set((()=>this._renderer.value.handleResize(e,t))):this._renderer.value.handleResize(e,t),this._fullRefresh())}handleCharSizeChanged(){var e;null===(e=this._renderer.value)||void 0===e||e.handleCharSizeChanged()}handleBlur(){var e;null===(e=this._renderer.value)||void 0===e||e.handleBlur()}handleFocus(){var e;null===(e=this._renderer.value)||void 0===e||e.handleFocus()}handleSelectionChanged(e,t,i){var s;this._selectionState.start=e,this._selectionState.end=t,this._selectionState.columnSelectMode=i,null===(s=this._renderer.value)||void 0===s||s.handleSelectionChanged(e,t,i)}handleCursorMove(){var e;null===(e=this._renderer.value)||void 0===e||e.handleCursorMove()}clear(){var e;null===(e=this._renderer.value)||void 0===e||e.clear()}};t.RenderService=u=s([r(2,_.IOptionsService),r(3,h.ICharSizeService),r(4,_.IDecorationService),r(5,_.IBufferService),r(6,h.ICoreBrowserService),r(7,h.IThemeService)],u)},9312:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.SelectionService=void 0;const n=i(9806),o=i(9504),a=i(456),h=i(4725),c=i(8460),l=i(844),d=i(6114),_=i(4841),u=i(511),f=i(2585),v=String.fromCharCode(160),p=new RegExp(v,"g");let g=t.SelectionService=class extends l.Disposable{constructor(e,t,i,s,r,n,o,h,d){super(),this._element=e,this._screenElement=t,this._linkifier=i,this._bufferService=s,this._coreService=r,this._mouseService=n,this._optionsService=o,this._renderService=h,this._coreBrowserService=d,this._dragScrollAmount=0,this._enabled=!0,this._workCell=new u.CellData,this._mouseDownTimeStamp=0,this._oldHasSelection=!1,this._oldSelectionStart=void 0,this._oldSelectionEnd=void 0,this._onLinuxMouseSelection=this.register(new c.EventEmitter),this.onLinuxMouseSelection=this._onLinuxMouseSelection.event,this._onRedrawRequest=this.register(new c.EventEmitter),this.onRequestRedraw=this._onRedrawRequest.event,this._onSelectionChange=this.register(new c.EventEmitter),this.onSelectionChange=this._onSelectionChange.event,this._onRequestScrollLines=this.register(new c.EventEmitter),this.onRequestScrollLines=this._onRequestScrollLines.event,this._mouseMoveListener=e=>this._handleMouseMove(e),this._mouseUpListener=e=>this._handleMouseUp(e),this._coreService.onUserInput((()=>{this.hasSelection&&this.clearSelection()})),this._trimListener=this._bufferService.buffer.lines.onTrim((e=>this._handleTrim(e))),this.register(this._bufferService.buffers.onBufferActivate((e=>this._handleBufferActivate(e)))),this.enable(),this._model=new a.SelectionModel(this._bufferService),this._activeSelectionMode=0,this.register((0,l.toDisposable)((()=>{this._removeMouseDownListeners()})))}reset(){this.clearSelection()}disable(){this.clearSelection(),this._enabled=!1}enable(){this._enabled=!0}get selectionStart(){return this._model.finalSelectionStart}get selectionEnd(){return this._model.finalSelectionEnd}get hasSelection(){const e=this._model.finalSelectionStart,t=this._model.finalSelectionEnd;return!(!e||!t||e[0]===t[0]&&e[1]===t[1])}get selectionText(){const e=this._model.finalSelectionStart,t=this._model.finalSelectionEnd;if(!e||!t)return"";const i=this._bufferService.buffer,s=[];if(3===this._activeSelectionMode){if(e[0]===t[0])return"";const r=e[0]e.replace(p," "))).join(d.isWindows?"\r\n":"\n")}clearSelection(){this._model.clearSelection(),this._removeMouseDownListeners(),this.refresh(),this._onSelectionChange.fire()}refresh(e){this._refreshAnimationFrame||(this._refreshAnimationFrame=this._coreBrowserService.window.requestAnimationFrame((()=>this._refresh()))),d.isLinux&&e&&this.selectionText.length&&this._onLinuxMouseSelection.fire(this.selectionText)}_refresh(){this._refreshAnimationFrame=void 0,this._onRedrawRequest.fire({start:this._model.finalSelectionStart,end:this._model.finalSelectionEnd,columnSelectMode:3===this._activeSelectionMode})}_isClickInSelection(e){const t=this._getMouseBufferCoords(e),i=this._model.finalSelectionStart,s=this._model.finalSelectionEnd;return!!(i&&s&&t)&&this._areCoordsInSelection(t,i,s)}isCellInSelection(e,t){const i=this._model.finalSelectionStart,s=this._model.finalSelectionEnd;return!(!i||!s)&&this._areCoordsInSelection([e,t],i,s)}_areCoordsInSelection(e,t,i){return e[1]>t[1]&&e[1]=t[0]&&e[0]=t[0]}_selectWordAtCursor(e,t){var i,s;const r=null===(s=null===(i=this._linkifier.currentLink)||void 0===i?void 0:i.link)||void 0===s?void 0:s.range;if(r)return this._model.selectionStart=[r.start.x-1,r.start.y-1],this._model.selectionStartLength=(0,_.getRangeLength)(r,this._bufferService.cols),this._model.selectionEnd=void 0,!0;const n=this._getMouseBufferCoords(e);return!!n&&(this._selectWordAt(n,t),this._model.selectionEnd=void 0,!0)}selectAll(){this._model.isSelectAllActive=!0,this.refresh(),this._onSelectionChange.fire()}selectLines(e,t){this._model.clearSelection(),e=Math.max(e,0),t=Math.min(t,this._bufferService.buffer.lines.length-1),this._model.selectionStart=[0,e],this._model.selectionEnd=[this._bufferService.cols,t],this.refresh(),this._onSelectionChange.fire()}_handleTrim(e){this._model.handleTrim(e)&&this.refresh()}_getMouseBufferCoords(e){const t=this._mouseService.getCoords(e,this._screenElement,this._bufferService.cols,this._bufferService.rows,!0);if(t)return t[0]--,t[1]--,t[1]+=this._bufferService.buffer.ydisp,t}_getMouseEventScrollAmount(e){let t=(0,n.getCoordsRelativeToElement)(this._coreBrowserService.window,e,this._screenElement)[1];const i=this._renderService.dimensions.css.canvas.height;return t>=0&&t<=i?0:(t>i&&(t-=i),t=Math.min(Math.max(t,-50),50),t/=50,t/Math.abs(t)+Math.round(14*t))}shouldForceSelection(e){return d.isMac?e.altKey&&this._optionsService.rawOptions.macOptionClickForcesSelection:e.shiftKey}handleMouseDown(e){if(this._mouseDownTimeStamp=e.timeStamp,(2!==e.button||!this.hasSelection)&&0===e.button){if(!this._enabled){if(!this.shouldForceSelection(e))return;e.stopPropagation()}e.preventDefault(),this._dragScrollAmount=0,this._enabled&&e.shiftKey?this._handleIncrementalClick(e):1===e.detail?this._handleSingleClick(e):2===e.detail?this._handleDoubleClick(e):3===e.detail&&this._handleTripleClick(e),this._addMouseDownListeners(),this.refresh(!0)}}_addMouseDownListeners(){this._screenElement.ownerDocument&&(this._screenElement.ownerDocument.addEventListener("mousemove",this._mouseMoveListener),this._screenElement.ownerDocument.addEventListener("mouseup",this._mouseUpListener)),this._dragScrollIntervalTimer=this._coreBrowserService.window.setInterval((()=>this._dragScroll()),50)}_removeMouseDownListeners(){this._screenElement.ownerDocument&&(this._screenElement.ownerDocument.removeEventListener("mousemove",this._mouseMoveListener),this._screenElement.ownerDocument.removeEventListener("mouseup",this._mouseUpListener)),this._coreBrowserService.window.clearInterval(this._dragScrollIntervalTimer),this._dragScrollIntervalTimer=void 0}_handleIncrementalClick(e){this._model.selectionStart&&(this._model.selectionEnd=this._getMouseBufferCoords(e))}_handleSingleClick(e){if(this._model.selectionStartLength=0,this._model.isSelectAllActive=!1,this._activeSelectionMode=this.shouldColumnSelect(e)?3:0,this._model.selectionStart=this._getMouseBufferCoords(e),!this._model.selectionStart)return;this._model.selectionEnd=void 0;const t=this._bufferService.buffer.lines.get(this._model.selectionStart[1]);t&&t.length!==this._model.selectionStart[0]&&0===t.hasWidth(this._model.selectionStart[0])&&this._model.selectionStart[0]++}_handleDoubleClick(e){this._selectWordAtCursor(e,!0)&&(this._activeSelectionMode=1)}_handleTripleClick(e){const t=this._getMouseBufferCoords(e);t&&(this._activeSelectionMode=2,this._selectLineAt(t[1]))}shouldColumnSelect(e){return e.altKey&&!(d.isMac&&this._optionsService.rawOptions.macOptionClickForcesSelection)}_handleMouseMove(e){if(e.stopImmediatePropagation(),!this._model.selectionStart)return;const t=this._model.selectionEnd?[this._model.selectionEnd[0],this._model.selectionEnd[1]]:null;if(this._model.selectionEnd=this._getMouseBufferCoords(e),!this._model.selectionEnd)return void this.refresh(!0);2===this._activeSelectionMode?this._model.selectionEnd[1]0?this._model.selectionEnd[0]=this._bufferService.cols:this._dragScrollAmount<0&&(this._model.selectionEnd[0]=0));const i=this._bufferService.buffer;if(this._model.selectionEnd[1]0?(3!==this._activeSelectionMode&&(this._model.selectionEnd[0]=this._bufferService.cols),this._model.selectionEnd[1]=Math.min(e.ydisp+this._bufferService.rows,e.lines.length-1)):(3!==this._activeSelectionMode&&(this._model.selectionEnd[0]=0),this._model.selectionEnd[1]=e.ydisp),this.refresh()}}_handleMouseUp(e){const t=e.timeStamp-this._mouseDownTimeStamp;if(this._removeMouseDownListeners(),this.selectionText.length<=1&&t<500&&e.altKey&&this._optionsService.rawOptions.altClickMovesCursor){if(this._bufferService.buffer.ybase===this._bufferService.buffer.ydisp){const t=this._mouseService.getCoords(e,this._element,this._bufferService.cols,this._bufferService.rows,!1);if(t&&void 0!==t[0]&&void 0!==t[1]){const e=(0,o.moveToCellSequence)(t[0]-1,t[1]-1,this._bufferService,this._coreService.decPrivateModes.applicationCursorKeys);this._coreService.triggerDataEvent(e,!0)}}}else this._fireEventIfSelectionChanged()}_fireEventIfSelectionChanged(){const e=this._model.finalSelectionStart,t=this._model.finalSelectionEnd,i=!(!e||!t||e[0]===t[0]&&e[1]===t[1]);i?e&&t&&(this._oldSelectionStart&&this._oldSelectionEnd&&e[0]===this._oldSelectionStart[0]&&e[1]===this._oldSelectionStart[1]&&t[0]===this._oldSelectionEnd[0]&&t[1]===this._oldSelectionEnd[1]||this._fireOnSelectionChange(e,t,i)):this._oldHasSelection&&this._fireOnSelectionChange(e,t,i)}_fireOnSelectionChange(e,t,i){this._oldSelectionStart=e,this._oldSelectionEnd=t,this._oldHasSelection=i,this._onSelectionChange.fire()}_handleBufferActivate(e){this.clearSelection(),this._trimListener.dispose(),this._trimListener=e.activeBuffer.lines.onTrim((e=>this._handleTrim(e)))}_convertViewportColToCharacterIndex(e,t){let i=t;for(let s=0;t>=s;s++){const r=e.loadCell(s,this._workCell).getChars().length;0===this._workCell.getWidth()?i--:r>1&&t!==s&&(i+=r-1)}return i}setSelection(e,t,i){this._model.clearSelection(),this._removeMouseDownListeners(),this._model.selectionStart=[e,t],this._model.selectionStartLength=i,this.refresh(),this._fireEventIfSelectionChanged()}rightClickSelect(e){this._isClickInSelection(e)||(this._selectWordAtCursor(e,!1)&&this.refresh(!0),this._fireEventIfSelectionChanged())}_getWordAt(e,t,i=!0,s=!0){if(e[0]>=this._bufferService.cols)return;const r=this._bufferService.buffer,n=r.lines.get(e[1]);if(!n)return;const o=r.translateBufferLineToString(e[1],!1);let a=this._convertViewportColToCharacterIndex(n,e[0]),h=a;const c=e[0]-a;let l=0,d=0,_=0,u=0;if(" "===o.charAt(a)){for(;a>0&&" "===o.charAt(a-1);)a--;for(;h1&&(u+=s-1,h+=s-1);t>0&&a>0&&!this._isCharWordSeparator(n.loadCell(t-1,this._workCell));){n.loadCell(t-1,this._workCell);const e=this._workCell.getChars().length;0===this._workCell.getWidth()?(l++,t--):e>1&&(_+=e-1,a-=e-1),a--,t--}for(;i1&&(u+=e-1,h+=e-1),h++,i++}}h++;let f=a+c-l+_,v=Math.min(this._bufferService.cols,h-a+l+d-_-u);if(t||""!==o.slice(a,h).trim()){if(i&&0===f&&32!==n.getCodePoint(0)){const t=r.lines.get(e[1]-1);if(t&&n.isWrapped&&32!==t.getCodePoint(this._bufferService.cols-1)){const t=this._getWordAt([this._bufferService.cols-1,e[1]-1],!1,!0,!1);if(t){const e=this._bufferService.cols-t.start;f-=e,v+=e}}}if(s&&f+v===this._bufferService.cols&&32!==n.getCodePoint(this._bufferService.cols-1)){const t=r.lines.get(e[1]+1);if((null==t?void 0:t.isWrapped)&&32!==t.getCodePoint(0)){const t=this._getWordAt([0,e[1]+1],!1,!1,!0);t&&(v+=t.length)}}return{start:f,length:v}}}_selectWordAt(e,t){const i=this._getWordAt(e,t);if(i){for(;i.start<0;)i.start+=this._bufferService.cols,e[1]--;this._model.selectionStart=[i.start,e[1]],this._model.selectionStartLength=i.length}}_selectToWordAt(e){const t=this._getWordAt(e,!0);if(t){let i=e[1];for(;t.start<0;)t.start+=this._bufferService.cols,i--;if(!this._model.areSelectionValuesReversed())for(;t.start+t.length>this._bufferService.cols;)t.length-=this._bufferService.cols,i++;this._model.selectionEnd=[this._model.areSelectionValuesReversed()?t.start:t.start+t.length,i]}}_isCharWordSeparator(e){return 0!==e.getWidth()&&this._optionsService.rawOptions.wordSeparator.indexOf(e.getChars())>=0}_selectLineAt(e){const t=this._bufferService.buffer.getWrappedRangeForLine(e),i={start:{x:0,y:t.first},end:{x:this._bufferService.cols-1,y:t.last}};this._model.selectionStart=[0,t.first],this._model.selectionEnd=void 0,this._model.selectionStartLength=(0,_.getRangeLength)(i,this._bufferService.cols)}};t.SelectionService=g=s([r(3,f.IBufferService),r(4,f.ICoreService),r(5,h.IMouseService),r(6,f.IOptionsService),r(7,h.IRenderService),r(8,h.ICoreBrowserService)],g)},4725:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.IThemeService=t.ICharacterJoinerService=t.ISelectionService=t.IRenderService=t.IMouseService=t.ICoreBrowserService=t.ICharSizeService=void 0;const s=i(8343);t.ICharSizeService=(0,s.createDecorator)("CharSizeService"),t.ICoreBrowserService=(0,s.createDecorator)("CoreBrowserService"),t.IMouseService=(0,s.createDecorator)("MouseService"),t.IRenderService=(0,s.createDecorator)("RenderService"),t.ISelectionService=(0,s.createDecorator)("SelectionService"),t.ICharacterJoinerService=(0,s.createDecorator)("CharacterJoinerService"),t.IThemeService=(0,s.createDecorator)("ThemeService")},6731:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.ThemeService=t.DEFAULT_ANSI_COLORS=void 0;const n=i(7239),o=i(8055),a=i(8460),h=i(844),c=i(2585),l=o.css.toColor("#ffffff"),d=o.css.toColor("#000000"),_=o.css.toColor("#ffffff"),u=o.css.toColor("#000000"),f={css:"rgba(255, 255, 255, 0.3)",rgba:4294967117};t.DEFAULT_ANSI_COLORS=Object.freeze((()=>{const e=[o.css.toColor("#2e3436"),o.css.toColor("#cc0000"),o.css.toColor("#4e9a06"),o.css.toColor("#c4a000"),o.css.toColor("#3465a4"),o.css.toColor("#75507b"),o.css.toColor("#06989a"),o.css.toColor("#d3d7cf"),o.css.toColor("#555753"),o.css.toColor("#ef2929"),o.css.toColor("#8ae234"),o.css.toColor("#fce94f"),o.css.toColor("#729fcf"),o.css.toColor("#ad7fa8"),o.css.toColor("#34e2e2"),o.css.toColor("#eeeeec")],t=[0,95,135,175,215,255];for(let i=0;i<216;i++){const s=t[i/36%6|0],r=t[i/6%6|0],n=t[i%6];e.push({css:o.channels.toCss(s,r,n),rgba:o.channels.toRgba(s,r,n)})}for(let t=0;t<24;t++){const i=8+10*t;e.push({css:o.channels.toCss(i,i,i),rgba:o.channels.toRgba(i,i,i)})}return e})());let v=t.ThemeService=class extends h.Disposable{get colors(){return this._colors}constructor(e){super(),this._optionsService=e,this._contrastCache=new n.ColorContrastCache,this._halfContrastCache=new n.ColorContrastCache,this._onChangeColors=this.register(new a.EventEmitter),this.onChangeColors=this._onChangeColors.event,this._colors={foreground:l,background:d,cursor:_,cursorAccent:u,selectionForeground:void 0,selectionBackgroundTransparent:f,selectionBackgroundOpaque:o.color.blend(d,f),selectionInactiveBackgroundTransparent:f,selectionInactiveBackgroundOpaque:o.color.blend(d,f),ansi:t.DEFAULT_ANSI_COLORS.slice(),contrastCache:this._contrastCache,halfContrastCache:this._halfContrastCache},this._updateRestoreColors(),this._setTheme(this._optionsService.rawOptions.theme),this.register(this._optionsService.onSpecificOptionChange("minimumContrastRatio",(()=>this._contrastCache.clear()))),this.register(this._optionsService.onSpecificOptionChange("theme",(()=>this._setTheme(this._optionsService.rawOptions.theme))))}_setTheme(e={}){const i=this._colors;if(i.foreground=p(e.foreground,l),i.background=p(e.background,d),i.cursor=p(e.cursor,_),i.cursorAccent=p(e.cursorAccent,u),i.selectionBackgroundTransparent=p(e.selectionBackground,f),i.selectionBackgroundOpaque=o.color.blend(i.background,i.selectionBackgroundTransparent),i.selectionInactiveBackgroundTransparent=p(e.selectionInactiveBackground,i.selectionBackgroundTransparent),i.selectionInactiveBackgroundOpaque=o.color.blend(i.background,i.selectionInactiveBackgroundTransparent),i.selectionForeground=e.selectionForeground?p(e.selectionForeground,o.NULL_COLOR):void 0,i.selectionForeground===o.NULL_COLOR&&(i.selectionForeground=void 0),o.color.isOpaque(i.selectionBackgroundTransparent)){const e=.3;i.selectionBackgroundTransparent=o.color.opacity(i.selectionBackgroundTransparent,e)}if(o.color.isOpaque(i.selectionInactiveBackgroundTransparent)){const e=.3;i.selectionInactiveBackgroundTransparent=o.color.opacity(i.selectionInactiveBackgroundTransparent,e)}if(i.ansi=t.DEFAULT_ANSI_COLORS.slice(),i.ansi[0]=p(e.black,t.DEFAULT_ANSI_COLORS[0]),i.ansi[1]=p(e.red,t.DEFAULT_ANSI_COLORS[1]),i.ansi[2]=p(e.green,t.DEFAULT_ANSI_COLORS[2]),i.ansi[3]=p(e.yellow,t.DEFAULT_ANSI_COLORS[3]),i.ansi[4]=p(e.blue,t.DEFAULT_ANSI_COLORS[4]),i.ansi[5]=p(e.magenta,t.DEFAULT_ANSI_COLORS[5]),i.ansi[6]=p(e.cyan,t.DEFAULT_ANSI_COLORS[6]),i.ansi[7]=p(e.white,t.DEFAULT_ANSI_COLORS[7]),i.ansi[8]=p(e.brightBlack,t.DEFAULT_ANSI_COLORS[8]),i.ansi[9]=p(e.brightRed,t.DEFAULT_ANSI_COLORS[9]),i.ansi[10]=p(e.brightGreen,t.DEFAULT_ANSI_COLORS[10]),i.ansi[11]=p(e.brightYellow,t.DEFAULT_ANSI_COLORS[11]),i.ansi[12]=p(e.brightBlue,t.DEFAULT_ANSI_COLORS[12]),i.ansi[13]=p(e.brightMagenta,t.DEFAULT_ANSI_COLORS[13]),i.ansi[14]=p(e.brightCyan,t.DEFAULT_ANSI_COLORS[14]),i.ansi[15]=p(e.brightWhite,t.DEFAULT_ANSI_COLORS[15]),e.extendedAnsi){const s=Math.min(i.ansi.length-16,e.extendedAnsi.length);for(let r=0;r{Object.defineProperty(t,"__esModule",{value:!0}),t.CircularList=void 0;const s=i(8460),r=i(844);class n extends r.Disposable{constructor(e){super(),this._maxLength=e,this.onDeleteEmitter=this.register(new s.EventEmitter),this.onDelete=this.onDeleteEmitter.event,this.onInsertEmitter=this.register(new s.EventEmitter),this.onInsert=this.onInsertEmitter.event,this.onTrimEmitter=this.register(new s.EventEmitter),this.onTrim=this.onTrimEmitter.event,this._array=new Array(this._maxLength),this._startIndex=0,this._length=0}get maxLength(){return this._maxLength}set maxLength(e){if(this._maxLength===e)return;const t=new Array(e);for(let i=0;ithis._length)for(let t=this._length;t=e;t--)this._array[this._getCyclicIndex(t+i.length)]=this._array[this._getCyclicIndex(t)];for(let t=0;tthis._maxLength){const e=this._length+i.length-this._maxLength;this._startIndex+=e,this._length=this._maxLength,this.onTrimEmitter.fire(e)}else this._length+=i.length}trimStart(e){e>this._length&&(e=this._length),this._startIndex+=e,this._length-=e,this.onTrimEmitter.fire(e)}shiftElements(e,t,i){if(!(t<=0)){if(e<0||e>=this._length)throw new Error("start argument out of range");if(e+i<0)throw new Error("Cannot shift elements in list beyond index 0");if(i>0){for(let s=t-1;s>=0;s--)this.set(e+s+i,this.get(e+s));const s=e+t+i-this._length;if(s>0)for(this._length+=s;this._length>this._maxLength;)this._length--,this._startIndex++,this.onTrimEmitter.fire(1)}else for(let s=0;s{Object.defineProperty(t,"__esModule",{value:!0}),t.clone=void 0,t.clone=function e(t,i=5){if("object"!=typeof t)return t;const s=Array.isArray(t)?[]:{};for(const r in t)s[r]=i<=1?t[r]:t[r]&&e(t[r],i-1);return s}},8055:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.contrastRatio=t.toPaddedHex=t.rgba=t.rgb=t.css=t.color=t.channels=t.NULL_COLOR=void 0;const s=i(6114);let r=0,n=0,o=0,a=0;var h,c,l,d,_;function u(e){const t=e.toString(16);return t.length<2?"0"+t:t}function f(e,t){return e>>0}}(h||(t.channels=h={})),function(e){function t(e,t){return a=Math.round(255*t),[r,n,o]=_.toChannels(e.rgba),{css:h.toCss(r,n,o,a),rgba:h.toRgba(r,n,o,a)}}e.blend=function(e,t){if(a=(255&t.rgba)/255,1===a)return{css:t.css,rgba:t.rgba};const i=t.rgba>>24&255,s=t.rgba>>16&255,c=t.rgba>>8&255,l=e.rgba>>24&255,d=e.rgba>>16&255,_=e.rgba>>8&255;return r=l+Math.round((i-l)*a),n=d+Math.round((s-d)*a),o=_+Math.round((c-_)*a),{css:h.toCss(r,n,o),rgba:h.toRgba(r,n,o)}},e.isOpaque=function(e){return 255==(255&e.rgba)},e.ensureContrastRatio=function(e,t,i){const s=_.ensureContrastRatio(e.rgba,t.rgba,i);if(s)return _.toColor(s>>24&255,s>>16&255,s>>8&255)},e.opaque=function(e){const t=(255|e.rgba)>>>0;return[r,n,o]=_.toChannels(t),{css:h.toCss(r,n,o),rgba:t}},e.opacity=t,e.multiplyOpacity=function(e,i){return a=255&e.rgba,t(e,a*i/255)},e.toColorRGB=function(e){return[e.rgba>>24&255,e.rgba>>16&255,e.rgba>>8&255]}}(c||(t.color=c={})),function(e){let t,i;if(!s.isNode){const e=document.createElement("canvas");e.width=1,e.height=1;const s=e.getContext("2d",{willReadFrequently:!0});s&&(t=s,t.globalCompositeOperation="copy",i=t.createLinearGradient(0,0,1,1))}e.toColor=function(e){if(e.match(/#[\da-f]{3,8}/i))switch(e.length){case 4:return r=parseInt(e.slice(1,2).repeat(2),16),n=parseInt(e.slice(2,3).repeat(2),16),o=parseInt(e.slice(3,4).repeat(2),16),_.toColor(r,n,o);case 5:return r=parseInt(e.slice(1,2).repeat(2),16),n=parseInt(e.slice(2,3).repeat(2),16),o=parseInt(e.slice(3,4).repeat(2),16),a=parseInt(e.slice(4,5).repeat(2),16),_.toColor(r,n,o,a);case 7:return{css:e,rgba:(parseInt(e.slice(1),16)<<8|255)>>>0};case 9:return{css:e,rgba:parseInt(e.slice(1),16)>>>0}}const s=e.match(/rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(,\s*(0|1|\d?\.(\d+))\s*)?\)/);if(s)return r=parseInt(s[1]),n=parseInt(s[2]),o=parseInt(s[3]),a=Math.round(255*(void 0===s[5]?1:parseFloat(s[5]))),_.toColor(r,n,o,a);if(!t||!i)throw new Error("css.toColor: Unsupported css format");if(t.fillStyle=i,t.fillStyle=e,"string"!=typeof t.fillStyle)throw new Error("css.toColor: Unsupported css format");if(t.fillRect(0,0,1,1),[r,n,o,a]=t.getImageData(0,0,1,1).data,255!==a)throw new Error("css.toColor: Unsupported css format");return{rgba:h.toRgba(r,n,o,a),css:e}}}(l||(t.css=l={})),function(e){function t(e,t,i){const s=e/255,r=t/255,n=i/255;return.2126*(s<=.03928?s/12.92:Math.pow((s+.055)/1.055,2.4))+.7152*(r<=.03928?r/12.92:Math.pow((r+.055)/1.055,2.4))+.0722*(n<=.03928?n/12.92:Math.pow((n+.055)/1.055,2.4))}e.relativeLuminance=function(e){return t(e>>16&255,e>>8&255,255&e)},e.relativeLuminance2=t}(d||(t.rgb=d={})),function(e){function t(e,t,i){const s=e>>24&255,r=e>>16&255,n=e>>8&255;let o=t>>24&255,a=t>>16&255,h=t>>8&255,c=f(d.relativeLuminance2(o,a,h),d.relativeLuminance2(s,r,n));for(;c0||a>0||h>0);)o-=Math.max(0,Math.ceil(.1*o)),a-=Math.max(0,Math.ceil(.1*a)),h-=Math.max(0,Math.ceil(.1*h)),c=f(d.relativeLuminance2(o,a,h),d.relativeLuminance2(s,r,n));return(o<<24|a<<16|h<<8|255)>>>0}function i(e,t,i){const s=e>>24&255,r=e>>16&255,n=e>>8&255;let o=t>>24&255,a=t>>16&255,h=t>>8&255,c=f(d.relativeLuminance2(o,a,h),d.relativeLuminance2(s,r,n));for(;c>>0}e.ensureContrastRatio=function(e,s,r){const n=d.relativeLuminance(e>>8),o=d.relativeLuminance(s>>8);if(f(n,o)>8));if(af(n,d.relativeLuminance(t>>8))?o:t}return o}const a=i(e,s,r),h=f(n,d.relativeLuminance(a>>8));if(hf(n,d.relativeLuminance(i>>8))?a:i}return a}},e.reduceLuminance=t,e.increaseLuminance=i,e.toChannels=function(e){return[e>>24&255,e>>16&255,e>>8&255,255&e]},e.toColor=function(e,t,i,s){return{css:h.toCss(e,t,i,s),rgba:h.toRgba(e,t,i,s)}}}(_||(t.rgba=_={})),t.toPaddedHex=u,t.contrastRatio=f},8969:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.CoreTerminal=void 0;const s=i(844),r=i(2585),n=i(4348),o=i(7866),a=i(744),h=i(7302),c=i(6975),l=i(8460),d=i(1753),_=i(1480),u=i(7994),f=i(9282),v=i(5435),p=i(5981),g=i(2660);let m=!1;class S extends s.Disposable{get onScroll(){return this._onScrollApi||(this._onScrollApi=this.register(new l.EventEmitter),this._onScroll.event((e=>{var t;null===(t=this._onScrollApi)||void 0===t||t.fire(e.position)}))),this._onScrollApi.event}get cols(){return this._bufferService.cols}get rows(){return this._bufferService.rows}get buffers(){return this._bufferService.buffers}get options(){return this.optionsService.options}set options(e){for(const t in e)this.optionsService.options[t]=e[t]}constructor(e){super(),this._windowsWrappingHeuristics=this.register(new s.MutableDisposable),this._onBinary=this.register(new l.EventEmitter),this.onBinary=this._onBinary.event,this._onData=this.register(new l.EventEmitter),this.onData=this._onData.event,this._onLineFeed=this.register(new l.EventEmitter),this.onLineFeed=this._onLineFeed.event,this._onResize=this.register(new l.EventEmitter),this.onResize=this._onResize.event,this._onWriteParsed=this.register(new l.EventEmitter),this.onWriteParsed=this._onWriteParsed.event,this._onScroll=this.register(new l.EventEmitter),this._instantiationService=new n.InstantiationService,this.optionsService=this.register(new h.OptionsService(e)),this._instantiationService.setService(r.IOptionsService,this.optionsService),this._bufferService=this.register(this._instantiationService.createInstance(a.BufferService)),this._instantiationService.setService(r.IBufferService,this._bufferService),this._logService=this.register(this._instantiationService.createInstance(o.LogService)),this._instantiationService.setService(r.ILogService,this._logService),this.coreService=this.register(this._instantiationService.createInstance(c.CoreService)),this._instantiationService.setService(r.ICoreService,this.coreService),this.coreMouseService=this.register(this._instantiationService.createInstance(d.CoreMouseService)),this._instantiationService.setService(r.ICoreMouseService,this.coreMouseService),this.unicodeService=this.register(this._instantiationService.createInstance(_.UnicodeService)),this._instantiationService.setService(r.IUnicodeService,this.unicodeService),this._charsetService=this._instantiationService.createInstance(u.CharsetService),this._instantiationService.setService(r.ICharsetService,this._charsetService),this._oscLinkService=this._instantiationService.createInstance(g.OscLinkService),this._instantiationService.setService(r.IOscLinkService,this._oscLinkService),this._inputHandler=this.register(new v.InputHandler(this._bufferService,this._charsetService,this.coreService,this._logService,this.optionsService,this._oscLinkService,this.coreMouseService,this.unicodeService)),this.register((0,l.forwardEvent)(this._inputHandler.onLineFeed,this._onLineFeed)),this.register(this._inputHandler),this.register((0,l.forwardEvent)(this._bufferService.onResize,this._onResize)),this.register((0,l.forwardEvent)(this.coreService.onData,this._onData)),this.register((0,l.forwardEvent)(this.coreService.onBinary,this._onBinary)),this.register(this.coreService.onRequestScrollToBottom((()=>this.scrollToBottom()))),this.register(this.coreService.onUserInput((()=>this._writeBuffer.handleUserInput()))),this.register(this.optionsService.onMultipleOptionChange(["windowsMode","windowsPty"],(()=>this._handleWindowsPtyOptionChange()))),this.register(this._bufferService.onScroll((e=>{this._onScroll.fire({position:this._bufferService.buffer.ydisp,source:0}),this._inputHandler.markRangeDirty(this._bufferService.buffer.scrollTop,this._bufferService.buffer.scrollBottom)}))),this.register(this._inputHandler.onScroll((e=>{this._onScroll.fire({position:this._bufferService.buffer.ydisp,source:0}),this._inputHandler.markRangeDirty(this._bufferService.buffer.scrollTop,this._bufferService.buffer.scrollBottom)}))),this._writeBuffer=this.register(new p.WriteBuffer(((e,t)=>this._inputHandler.parse(e,t)))),this.register((0,l.forwardEvent)(this._writeBuffer.onWriteParsed,this._onWriteParsed))}write(e,t){this._writeBuffer.write(e,t)}writeSync(e,t){this._logService.logLevel<=r.LogLevelEnum.WARN&&!m&&(this._logService.warn("writeSync is unreliable and will be removed soon."),m=!0),this._writeBuffer.writeSync(e,t)}resize(e,t){isNaN(e)||isNaN(t)||(e=Math.max(e,a.MINIMUM_COLS),t=Math.max(t,a.MINIMUM_ROWS),this._bufferService.resize(e,t))}scroll(e,t=!1){this._bufferService.scroll(e,t)}scrollLines(e,t,i){this._bufferService.scrollLines(e,t,i)}scrollPages(e){this.scrollLines(e*(this.rows-1))}scrollToTop(){this.scrollLines(-this._bufferService.buffer.ydisp)}scrollToBottom(){this.scrollLines(this._bufferService.buffer.ybase-this._bufferService.buffer.ydisp)}scrollToLine(e){const t=e-this._bufferService.buffer.ydisp;0!==t&&this.scrollLines(t)}registerEscHandler(e,t){return this._inputHandler.registerEscHandler(e,t)}registerDcsHandler(e,t){return this._inputHandler.registerDcsHandler(e,t)}registerCsiHandler(e,t){return this._inputHandler.registerCsiHandler(e,t)}registerOscHandler(e,t){return this._inputHandler.registerOscHandler(e,t)}_setup(){this._handleWindowsPtyOptionChange()}reset(){this._inputHandler.reset(),this._bufferService.reset(),this._charsetService.reset(),this.coreService.reset(),this.coreMouseService.reset()}_handleWindowsPtyOptionChange(){let e=!1;const t=this.optionsService.rawOptions.windowsPty;t&&void 0!==t.buildNumber&&void 0!==t.buildNumber?e=!!("conpty"===t.backend&&t.buildNumber<21376):this.optionsService.rawOptions.windowsMode&&(e=!0),e?this._enableWindowsWrappingHeuristics():this._windowsWrappingHeuristics.clear()}_enableWindowsWrappingHeuristics(){if(!this._windowsWrappingHeuristics.value){const e=[];e.push(this.onLineFeed(f.updateWindowsModeWrappedState.bind(null,this._bufferService))),e.push(this.registerCsiHandler({final:"H"},(()=>((0,f.updateWindowsModeWrappedState)(this._bufferService),!1)))),this._windowsWrappingHeuristics.value=(0,s.toDisposable)((()=>{for(const t of e)t.dispose()}))}}}t.CoreTerminal=S},8460:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.forwardEvent=t.EventEmitter=void 0,t.EventEmitter=class{constructor(){this._listeners=[],this._disposed=!1}get event(){return this._event||(this._event=e=>(this._listeners.push(e),{dispose:()=>{if(!this._disposed)for(let t=0;tt.fire(e)))}},5435:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.InputHandler=t.WindowsOptionsReportType=void 0;const n=i(2584),o=i(7116),a=i(2015),h=i(844),c=i(482),l=i(8437),d=i(8460),_=i(643),u=i(511),f=i(3734),v=i(2585),p=i(6242),g=i(6351),m=i(5941),S={"(":0,")":1,"*":2,"+":3,"-":1,".":2},C=131072;function b(e,t){if(e>24)return t.setWinLines||!1;switch(e){case 1:return!!t.restoreWin;case 2:return!!t.minimizeWin;case 3:return!!t.setWinPosition;case 4:return!!t.setWinSizePixels;case 5:return!!t.raiseWin;case 6:return!!t.lowerWin;case 7:return!!t.refreshWin;case 8:return!!t.setWinSizeChars;case 9:return!!t.maximizeWin;case 10:return!!t.fullscreenWin;case 11:return!!t.getWinState;case 13:return!!t.getWinPosition;case 14:return!!t.getWinSizePixels;case 15:return!!t.getScreenSizePixels;case 16:return!!t.getCellSizePixels;case 18:return!!t.getWinSizeChars;case 19:return!!t.getScreenSizeChars;case 20:return!!t.getIconTitle;case 21:return!!t.getWinTitle;case 22:return!!t.pushTitle;case 23:return!!t.popTitle;case 24:return!!t.setWinLines}return!1}var y;!function(e){e[e.GET_WIN_SIZE_PIXELS=0]="GET_WIN_SIZE_PIXELS",e[e.GET_CELL_SIZE_PIXELS=1]="GET_CELL_SIZE_PIXELS"}(y||(t.WindowsOptionsReportType=y={}));let w=0;class E extends h.Disposable{getAttrData(){return this._curAttrData}constructor(e,t,i,s,r,h,_,f,v=new a.EscapeSequenceParser){super(),this._bufferService=e,this._charsetService=t,this._coreService=i,this._logService=s,this._optionsService=r,this._oscLinkService=h,this._coreMouseService=_,this._unicodeService=f,this._parser=v,this._parseBuffer=new Uint32Array(4096),this._stringDecoder=new c.StringToUtf32,this._utf8Decoder=new c.Utf8ToUtf32,this._workCell=new u.CellData,this._windowTitle="",this._iconName="",this._windowTitleStack=[],this._iconNameStack=[],this._curAttrData=l.DEFAULT_ATTR_DATA.clone(),this._eraseAttrDataInternal=l.DEFAULT_ATTR_DATA.clone(),this._onRequestBell=this.register(new d.EventEmitter),this.onRequestBell=this._onRequestBell.event,this._onRequestRefreshRows=this.register(new d.EventEmitter),this.onRequestRefreshRows=this._onRequestRefreshRows.event,this._onRequestReset=this.register(new d.EventEmitter),this.onRequestReset=this._onRequestReset.event,this._onRequestSendFocus=this.register(new d.EventEmitter),this.onRequestSendFocus=this._onRequestSendFocus.event,this._onRequestSyncScrollBar=this.register(new d.EventEmitter),this.onRequestSyncScrollBar=this._onRequestSyncScrollBar.event,this._onRequestWindowsOptionsReport=this.register(new d.EventEmitter),this.onRequestWindowsOptionsReport=this._onRequestWindowsOptionsReport.event,this._onA11yChar=this.register(new d.EventEmitter),this.onA11yChar=this._onA11yChar.event,this._onA11yTab=this.register(new d.EventEmitter),this.onA11yTab=this._onA11yTab.event,this._onCursorMove=this.register(new d.EventEmitter),this.onCursorMove=this._onCursorMove.event,this._onLineFeed=this.register(new d.EventEmitter),this.onLineFeed=this._onLineFeed.event,this._onScroll=this.register(new d.EventEmitter),this.onScroll=this._onScroll.event,this._onTitleChange=this.register(new d.EventEmitter),this.onTitleChange=this._onTitleChange.event,this._onColor=this.register(new d.EventEmitter),this.onColor=this._onColor.event,this._parseStack={paused:!1,cursorStartX:0,cursorStartY:0,decodedLength:0,position:0},this._specialColors=[256,257,258],this.register(this._parser),this._dirtyRowTracker=new k(this._bufferService),this._activeBuffer=this._bufferService.buffer,this.register(this._bufferService.buffers.onBufferActivate((e=>this._activeBuffer=e.activeBuffer))),this._parser.setCsiHandlerFallback(((e,t)=>{this._logService.debug("Unknown CSI code: ",{identifier:this._parser.identToString(e),params:t.toArray()})})),this._parser.setEscHandlerFallback((e=>{this._logService.debug("Unknown ESC code: ",{identifier:this._parser.identToString(e)})})),this._parser.setExecuteHandlerFallback((e=>{this._logService.debug("Unknown EXECUTE code: ",{code:e})})),this._parser.setOscHandlerFallback(((e,t,i)=>{this._logService.debug("Unknown OSC code: ",{identifier:e,action:t,data:i})})),this._parser.setDcsHandlerFallback(((e,t,i)=>{"HOOK"===t&&(i=i.toArray()),this._logService.debug("Unknown DCS code: ",{identifier:this._parser.identToString(e),action:t,payload:i})})),this._parser.setPrintHandler(((e,t,i)=>this.print(e,t,i))),this._parser.registerCsiHandler({final:"@"},(e=>this.insertChars(e))),this._parser.registerCsiHandler({intermediates:" ",final:"@"},(e=>this.scrollLeft(e))),this._parser.registerCsiHandler({final:"A"},(e=>this.cursorUp(e))),this._parser.registerCsiHandler({intermediates:" ",final:"A"},(e=>this.scrollRight(e))),this._parser.registerCsiHandler({final:"B"},(e=>this.cursorDown(e))),this._parser.registerCsiHandler({final:"C"},(e=>this.cursorForward(e))),this._parser.registerCsiHandler({final:"D"},(e=>this.cursorBackward(e))),this._parser.registerCsiHandler({final:"E"},(e=>this.cursorNextLine(e))),this._parser.registerCsiHandler({final:"F"},(e=>this.cursorPrecedingLine(e))),this._parser.registerCsiHandler({final:"G"},(e=>this.cursorCharAbsolute(e))),this._parser.registerCsiHandler({final:"H"},(e=>this.cursorPosition(e))),this._parser.registerCsiHandler({final:"I"},(e=>this.cursorForwardTab(e))),this._parser.registerCsiHandler({final:"J"},(e=>this.eraseInDisplay(e,!1))),this._parser.registerCsiHandler({prefix:"?",final:"J"},(e=>this.eraseInDisplay(e,!0))),this._parser.registerCsiHandler({final:"K"},(e=>this.eraseInLine(e,!1))),this._parser.registerCsiHandler({prefix:"?",final:"K"},(e=>this.eraseInLine(e,!0))),this._parser.registerCsiHandler({final:"L"},(e=>this.insertLines(e))),this._parser.registerCsiHandler({final:"M"},(e=>this.deleteLines(e))),this._parser.registerCsiHandler({final:"P"},(e=>this.deleteChars(e))),this._parser.registerCsiHandler({final:"S"},(e=>this.scrollUp(e))),this._parser.registerCsiHandler({final:"T"},(e=>this.scrollDown(e))),this._parser.registerCsiHandler({final:"X"},(e=>this.eraseChars(e))),this._parser.registerCsiHandler({final:"Z"},(e=>this.cursorBackwardTab(e))),this._parser.registerCsiHandler({final:"`"},(e=>this.charPosAbsolute(e))),this._parser.registerCsiHandler({final:"a"},(e=>this.hPositionRelative(e))),this._parser.registerCsiHandler({final:"b"},(e=>this.repeatPrecedingCharacter(e))),this._parser.registerCsiHandler({final:"c"},(e=>this.sendDeviceAttributesPrimary(e))),this._parser.registerCsiHandler({prefix:">",final:"c"},(e=>this.sendDeviceAttributesSecondary(e))),this._parser.registerCsiHandler({final:"d"},(e=>this.linePosAbsolute(e))),this._parser.registerCsiHandler({final:"e"},(e=>this.vPositionRelative(e))),this._parser.registerCsiHandler({final:"f"},(e=>this.hVPosition(e))),this._parser.registerCsiHandler({final:"g"},(e=>this.tabClear(e))),this._parser.registerCsiHandler({final:"h"},(e=>this.setMode(e))),this._parser.registerCsiHandler({prefix:"?",final:"h"},(e=>this.setModePrivate(e))),this._parser.registerCsiHandler({final:"l"},(e=>this.resetMode(e))),this._parser.registerCsiHandler({prefix:"?",final:"l"},(e=>this.resetModePrivate(e))),this._parser.registerCsiHandler({final:"m"},(e=>this.charAttributes(e))),this._parser.registerCsiHandler({final:"n"},(e=>this.deviceStatus(e))),this._parser.registerCsiHandler({prefix:"?",final:"n"},(e=>this.deviceStatusPrivate(e))),this._parser.registerCsiHandler({intermediates:"!",final:"p"},(e=>this.softReset(e))),this._parser.registerCsiHandler({intermediates:" ",final:"q"},(e=>this.setCursorStyle(e))),this._parser.registerCsiHandler({final:"r"},(e=>this.setScrollRegion(e))),this._parser.registerCsiHandler({final:"s"},(e=>this.saveCursor(e))),this._parser.registerCsiHandler({final:"t"},(e=>this.windowOptions(e))),this._parser.registerCsiHandler({final:"u"},(e=>this.restoreCursor(e))),this._parser.registerCsiHandler({intermediates:"'",final:"}"},(e=>this.insertColumns(e))),this._parser.registerCsiHandler({intermediates:"'",final:"~"},(e=>this.deleteColumns(e))),this._parser.registerCsiHandler({intermediates:'"',final:"q"},(e=>this.selectProtected(e))),this._parser.registerCsiHandler({intermediates:"$",final:"p"},(e=>this.requestMode(e,!0))),this._parser.registerCsiHandler({prefix:"?",intermediates:"$",final:"p"},(e=>this.requestMode(e,!1))),this._parser.setExecuteHandler(n.C0.BEL,(()=>this.bell())),this._parser.setExecuteHandler(n.C0.LF,(()=>this.lineFeed())),this._parser.setExecuteHandler(n.C0.VT,(()=>this.lineFeed())),this._parser.setExecuteHandler(n.C0.FF,(()=>this.lineFeed())),this._parser.setExecuteHandler(n.C0.CR,(()=>this.carriageReturn())),this._parser.setExecuteHandler(n.C0.BS,(()=>this.backspace())),this._parser.setExecuteHandler(n.C0.HT,(()=>this.tab())),this._parser.setExecuteHandler(n.C0.SO,(()=>this.shiftOut())),this._parser.setExecuteHandler(n.C0.SI,(()=>this.shiftIn())),this._parser.setExecuteHandler(n.C1.IND,(()=>this.index())),this._parser.setExecuteHandler(n.C1.NEL,(()=>this.nextLine())),this._parser.setExecuteHandler(n.C1.HTS,(()=>this.tabSet())),this._parser.registerOscHandler(0,new p.OscHandler((e=>(this.setTitle(e),this.setIconName(e),!0)))),this._parser.registerOscHandler(1,new p.OscHandler((e=>this.setIconName(e)))),this._parser.registerOscHandler(2,new p.OscHandler((e=>this.setTitle(e)))),this._parser.registerOscHandler(4,new p.OscHandler((e=>this.setOrReportIndexedColor(e)))),this._parser.registerOscHandler(8,new p.OscHandler((e=>this.setHyperlink(e)))),this._parser.registerOscHandler(10,new p.OscHandler((e=>this.setOrReportFgColor(e)))),this._parser.registerOscHandler(11,new p.OscHandler((e=>this.setOrReportBgColor(e)))),this._parser.registerOscHandler(12,new p.OscHandler((e=>this.setOrReportCursorColor(e)))),this._parser.registerOscHandler(104,new p.OscHandler((e=>this.restoreIndexedColor(e)))),this._parser.registerOscHandler(110,new p.OscHandler((e=>this.restoreFgColor(e)))),this._parser.registerOscHandler(111,new p.OscHandler((e=>this.restoreBgColor(e)))),this._parser.registerOscHandler(112,new p.OscHandler((e=>this.restoreCursorColor(e)))),this._parser.registerEscHandler({final:"7"},(()=>this.saveCursor())),this._parser.registerEscHandler({final:"8"},(()=>this.restoreCursor())),this._parser.registerEscHandler({final:"D"},(()=>this.index())),this._parser.registerEscHandler({final:"E"},(()=>this.nextLine())),this._parser.registerEscHandler({final:"H"},(()=>this.tabSet())),this._parser.registerEscHandler({final:"M"},(()=>this.reverseIndex())),this._parser.registerEscHandler({final:"="},(()=>this.keypadApplicationMode())),this._parser.registerEscHandler({final:">"},(()=>this.keypadNumericMode())),this._parser.registerEscHandler({final:"c"},(()=>this.fullReset())),this._parser.registerEscHandler({final:"n"},(()=>this.setgLevel(2))),this._parser.registerEscHandler({final:"o"},(()=>this.setgLevel(3))),this._parser.registerEscHandler({final:"|"},(()=>this.setgLevel(3))),this._parser.registerEscHandler({final:"}"},(()=>this.setgLevel(2))),this._parser.registerEscHandler({final:"~"},(()=>this.setgLevel(1))),this._parser.registerEscHandler({intermediates:"%",final:"@"},(()=>this.selectDefaultCharset())),this._parser.registerEscHandler({intermediates:"%",final:"G"},(()=>this.selectDefaultCharset()));for(const e in o.CHARSETS)this._parser.registerEscHandler({intermediates:"(",final:e},(()=>this.selectCharset("("+e))),this._parser.registerEscHandler({intermediates:")",final:e},(()=>this.selectCharset(")"+e))),this._parser.registerEscHandler({intermediates:"*",final:e},(()=>this.selectCharset("*"+e))),this._parser.registerEscHandler({intermediates:"+",final:e},(()=>this.selectCharset("+"+e))),this._parser.registerEscHandler({intermediates:"-",final:e},(()=>this.selectCharset("-"+e))),this._parser.registerEscHandler({intermediates:".",final:e},(()=>this.selectCharset("."+e))),this._parser.registerEscHandler({intermediates:"/",final:e},(()=>this.selectCharset("/"+e)));this._parser.registerEscHandler({intermediates:"#",final:"8"},(()=>this.screenAlignmentPattern())),this._parser.setErrorHandler((e=>(this._logService.error("Parsing error: ",e),e))),this._parser.registerDcsHandler({intermediates:"$",final:"q"},new g.DcsHandler(((e,t)=>this.requestStatusString(e,t))))}_preserveStack(e,t,i,s){this._parseStack.paused=!0,this._parseStack.cursorStartX=e,this._parseStack.cursorStartY=t,this._parseStack.decodedLength=i,this._parseStack.position=s}_logSlowResolvingAsync(e){this._logService.logLevel<=v.LogLevelEnum.WARN&&Promise.race([e,new Promise(((e,t)=>setTimeout((()=>t("#SLOW_TIMEOUT")),5e3)))]).catch((e=>{if("#SLOW_TIMEOUT"!==e)throw e;console.warn("async parser handler taking longer than 5000 ms")}))}_getCurrentLinkId(){return this._curAttrData.extended.urlId}parse(e,t){let i,s=this._activeBuffer.x,r=this._activeBuffer.y,n=0;const o=this._parseStack.paused;if(o){if(i=this._parser.parse(this._parseBuffer,this._parseStack.decodedLength,t))return this._logSlowResolvingAsync(i),i;s=this._parseStack.cursorStartX,r=this._parseStack.cursorStartY,this._parseStack.paused=!1,e.length>C&&(n=this._parseStack.position+C)}if(this._logService.logLevel<=v.LogLevelEnum.DEBUG&&this._logService.debug("parsing data"+("string"==typeof e?` "${e}"`:` "${Array.prototype.map.call(e,(e=>String.fromCharCode(e))).join("")}"`),"string"==typeof e?e.split("").map((e=>e.charCodeAt(0))):e),this._parseBuffer.lengthC)for(let t=n;t0&&2===u.getWidth(this._activeBuffer.x-1)&&u.setCellFromCodePoint(this._activeBuffer.x-1,0,1,d.fg,d.bg,d.extended);for(let f=t;f=a)if(h){for(;this._activeBuffer.x=this._bufferService.rows&&(this._activeBuffer.y=this._bufferService.rows-1),this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y).isWrapped=!0),u=this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y)}else if(this._activeBuffer.x=a-1,2===r)continue;if(l&&(u.insertCells(this._activeBuffer.x,r,this._activeBuffer.getNullCell(d),d),2===u.getWidth(a-1)&&u.setCellFromCodePoint(a-1,_.NULL_CELL_CODE,_.NULL_CELL_WIDTH,d.fg,d.bg,d.extended)),u.setCellFromCodePoint(this._activeBuffer.x++,s,r,d.fg,d.bg,d.extended),r>0)for(;--r;)u.setCellFromCodePoint(this._activeBuffer.x++,0,0,d.fg,d.bg,d.extended)}else u.getWidth(this._activeBuffer.x-1)?u.addCodepointToCell(this._activeBuffer.x-1,s):u.addCodepointToCell(this._activeBuffer.x-2,s)}i-t>0&&(u.loadCell(this._activeBuffer.x-1,this._workCell),2===this._workCell.getWidth()||this._workCell.getCode()>65535?this._parser.precedingCodepoint=0:this._workCell.isCombined()?this._parser.precedingCodepoint=this._workCell.getChars().charCodeAt(0):this._parser.precedingCodepoint=this._workCell.content),this._activeBuffer.x0&&0===u.getWidth(this._activeBuffer.x)&&!u.hasContent(this._activeBuffer.x)&&u.setCellFromCodePoint(this._activeBuffer.x,0,1,d.fg,d.bg,d.extended),this._dirtyRowTracker.markDirty(this._activeBuffer.y)}registerCsiHandler(e,t){return"t"!==e.final||e.prefix||e.intermediates?this._parser.registerCsiHandler(e,t):this._parser.registerCsiHandler(e,(e=>!b(e.params[0],this._optionsService.rawOptions.windowOptions)||t(e)))}registerDcsHandler(e,t){return this._parser.registerDcsHandler(e,new g.DcsHandler(t))}registerEscHandler(e,t){return this._parser.registerEscHandler(e,t)}registerOscHandler(e,t){return this._parser.registerOscHandler(e,new p.OscHandler(t))}bell(){return this._onRequestBell.fire(),!0}lineFeed(){return this._dirtyRowTracker.markDirty(this._activeBuffer.y),this._optionsService.rawOptions.convertEol&&(this._activeBuffer.x=0),this._activeBuffer.y++,this._activeBuffer.y===this._activeBuffer.scrollBottom+1?(this._activeBuffer.y--,this._bufferService.scroll(this._eraseAttrData())):this._activeBuffer.y>=this._bufferService.rows?this._activeBuffer.y=this._bufferService.rows-1:this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y).isWrapped=!1,this._activeBuffer.x>=this._bufferService.cols&&this._activeBuffer.x--,this._dirtyRowTracker.markDirty(this._activeBuffer.y),this._onLineFeed.fire(),!0}carriageReturn(){return this._activeBuffer.x=0,!0}backspace(){var e;if(!this._coreService.decPrivateModes.reverseWraparound)return this._restrictCursor(),this._activeBuffer.x>0&&this._activeBuffer.x--,!0;if(this._restrictCursor(this._bufferService.cols),this._activeBuffer.x>0)this._activeBuffer.x--;else if(0===this._activeBuffer.x&&this._activeBuffer.y>this._activeBuffer.scrollTop&&this._activeBuffer.y<=this._activeBuffer.scrollBottom&&(null===(e=this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y))||void 0===e?void 0:e.isWrapped)){this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y).isWrapped=!1,this._activeBuffer.y--,this._activeBuffer.x=this._bufferService.cols-1;const e=this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y);e.hasWidth(this._activeBuffer.x)&&!e.hasContent(this._activeBuffer.x)&&this._activeBuffer.x--}return this._restrictCursor(),!0}tab(){if(this._activeBuffer.x>=this._bufferService.cols)return!0;const e=this._activeBuffer.x;return this._activeBuffer.x=this._activeBuffer.nextStop(),this._optionsService.rawOptions.screenReaderMode&&this._onA11yTab.fire(this._activeBuffer.x-e),!0}shiftOut(){return this._charsetService.setgLevel(1),!0}shiftIn(){return this._charsetService.setgLevel(0),!0}_restrictCursor(e=this._bufferService.cols-1){this._activeBuffer.x=Math.min(e,Math.max(0,this._activeBuffer.x)),this._activeBuffer.y=this._coreService.decPrivateModes.origin?Math.min(this._activeBuffer.scrollBottom,Math.max(this._activeBuffer.scrollTop,this._activeBuffer.y)):Math.min(this._bufferService.rows-1,Math.max(0,this._activeBuffer.y)),this._dirtyRowTracker.markDirty(this._activeBuffer.y)}_setCursor(e,t){this._dirtyRowTracker.markDirty(this._activeBuffer.y),this._coreService.decPrivateModes.origin?(this._activeBuffer.x=e,this._activeBuffer.y=this._activeBuffer.scrollTop+t):(this._activeBuffer.x=e,this._activeBuffer.y=t),this._restrictCursor(),this._dirtyRowTracker.markDirty(this._activeBuffer.y)}_moveCursor(e,t){this._restrictCursor(),this._setCursor(this._activeBuffer.x+e,this._activeBuffer.y+t)}cursorUp(e){const t=this._activeBuffer.y-this._activeBuffer.scrollTop;return t>=0?this._moveCursor(0,-Math.min(t,e.params[0]||1)):this._moveCursor(0,-(e.params[0]||1)),!0}cursorDown(e){const t=this._activeBuffer.scrollBottom-this._activeBuffer.y;return t>=0?this._moveCursor(0,Math.min(t,e.params[0]||1)):this._moveCursor(0,e.params[0]||1),!0}cursorForward(e){return this._moveCursor(e.params[0]||1,0),!0}cursorBackward(e){return this._moveCursor(-(e.params[0]||1),0),!0}cursorNextLine(e){return this.cursorDown(e),this._activeBuffer.x=0,!0}cursorPrecedingLine(e){return this.cursorUp(e),this._activeBuffer.x=0,!0}cursorCharAbsolute(e){return this._setCursor((e.params[0]||1)-1,this._activeBuffer.y),!0}cursorPosition(e){return this._setCursor(e.length>=2?(e.params[1]||1)-1:0,(e.params[0]||1)-1),!0}charPosAbsolute(e){return this._setCursor((e.params[0]||1)-1,this._activeBuffer.y),!0}hPositionRelative(e){return this._moveCursor(e.params[0]||1,0),!0}linePosAbsolute(e){return this._setCursor(this._activeBuffer.x,(e.params[0]||1)-1),!0}vPositionRelative(e){return this._moveCursor(0,e.params[0]||1),!0}hVPosition(e){return this.cursorPosition(e),!0}tabClear(e){const t=e.params[0];return 0===t?delete this._activeBuffer.tabs[this._activeBuffer.x]:3===t&&(this._activeBuffer.tabs={}),!0}cursorForwardTab(e){if(this._activeBuffer.x>=this._bufferService.cols)return!0;let t=e.params[0]||1;for(;t--;)this._activeBuffer.x=this._activeBuffer.nextStop();return!0}cursorBackwardTab(e){if(this._activeBuffer.x>=this._bufferService.cols)return!0;let t=e.params[0]||1;for(;t--;)this._activeBuffer.x=this._activeBuffer.prevStop();return!0}selectProtected(e){const t=e.params[0];return 1===t&&(this._curAttrData.bg|=536870912),2!==t&&0!==t||(this._curAttrData.bg&=-536870913),!0}_eraseInBufferLine(e,t,i,s=!1,r=!1){const n=this._activeBuffer.lines.get(this._activeBuffer.ybase+e);n.replaceCells(t,i,this._activeBuffer.getNullCell(this._eraseAttrData()),this._eraseAttrData(),r),s&&(n.isWrapped=!1)}_resetBufferLine(e,t=!1){const i=this._activeBuffer.lines.get(this._activeBuffer.ybase+e);i&&(i.fill(this._activeBuffer.getNullCell(this._eraseAttrData()),t),this._bufferService.buffer.clearMarkers(this._activeBuffer.ybase+e),i.isWrapped=!1)}eraseInDisplay(e,t=!1){let i;switch(this._restrictCursor(this._bufferService.cols),e.params[0]){case 0:for(i=this._activeBuffer.y,this._dirtyRowTracker.markDirty(i),this._eraseInBufferLine(i++,this._activeBuffer.x,this._bufferService.cols,0===this._activeBuffer.x,t);i=this._bufferService.cols&&(this._activeBuffer.lines.get(i+1).isWrapped=!1);i--;)this._resetBufferLine(i,t);this._dirtyRowTracker.markDirty(0);break;case 2:for(i=this._bufferService.rows,this._dirtyRowTracker.markDirty(i-1);i--;)this._resetBufferLine(i,t);this._dirtyRowTracker.markDirty(0);break;case 3:const e=this._activeBuffer.lines.length-this._bufferService.rows;e>0&&(this._activeBuffer.lines.trimStart(e),this._activeBuffer.ybase=Math.max(this._activeBuffer.ybase-e,0),this._activeBuffer.ydisp=Math.max(this._activeBuffer.ydisp-e,0),this._onScroll.fire(0))}return!0}eraseInLine(e,t=!1){switch(this._restrictCursor(this._bufferService.cols),e.params[0]){case 0:this._eraseInBufferLine(this._activeBuffer.y,this._activeBuffer.x,this._bufferService.cols,0===this._activeBuffer.x,t);break;case 1:this._eraseInBufferLine(this._activeBuffer.y,0,this._activeBuffer.x+1,!1,t);break;case 2:this._eraseInBufferLine(this._activeBuffer.y,0,this._bufferService.cols,!0,t)}return this._dirtyRowTracker.markDirty(this._activeBuffer.y),!0}insertLines(e){this._restrictCursor();let t=e.params[0]||1;if(this._activeBuffer.y>this._activeBuffer.scrollBottom||this._activeBuffer.ythis._activeBuffer.scrollBottom||this._activeBuffer.ythis._activeBuffer.scrollBottom||this._activeBuffer.ythis._activeBuffer.scrollBottom||this._activeBuffer.ythis._activeBuffer.scrollBottom||this._activeBuffer.ythis._activeBuffer.scrollBottom||this._activeBuffer.y0||(this._is("xterm")||this._is("rxvt-unicode")||this._is("screen")?this._coreService.triggerDataEvent(n.C0.ESC+"[?1;2c"):this._is("linux")&&this._coreService.triggerDataEvent(n.C0.ESC+"[?6c")),!0}sendDeviceAttributesSecondary(e){return e.params[0]>0||(this._is("xterm")?this._coreService.triggerDataEvent(n.C0.ESC+"[>0;276;0c"):this._is("rxvt-unicode")?this._coreService.triggerDataEvent(n.C0.ESC+"[>85;95;0c"):this._is("linux")?this._coreService.triggerDataEvent(e.params[0]+"c"):this._is("screen")&&this._coreService.triggerDataEvent(n.C0.ESC+"[>83;40003;0c")),!0}_is(e){return 0===(this._optionsService.rawOptions.termName+"").indexOf(e)}setMode(e){for(let t=0;te?1:2,u=e.params[0];return f=u,v=t?2===u?4:4===u?_(o.modes.insertMode):12===u?3:20===u?_(d.convertEol):0:1===u?_(i.applicationCursorKeys):3===u?d.windowOptions.setWinLines?80===h?2:132===h?1:0:0:6===u?_(i.origin):7===u?_(i.wraparound):8===u?3:9===u?_("X10"===s):12===u?_(d.cursorBlink):25===u?_(!o.isCursorHidden):45===u?_(i.reverseWraparound):66===u?_(i.applicationKeypad):67===u?4:1e3===u?_("VT200"===s):1002===u?_("DRAG"===s):1003===u?_("ANY"===s):1004===u?_(i.sendFocus):1005===u?4:1006===u?_("SGR"===r):1015===u?4:1016===u?_("SGR_PIXELS"===r):1048===u?1:47===u||1047===u||1049===u?_(c===l):2004===u?_(i.bracketedPasteMode):0,o.triggerDataEvent(`${n.C0.ESC}[${t?"":"?"}${f};${v}$y`),!0;var f,v}_updateAttrColor(e,t,i,s,r){return 2===t?(e|=50331648,e&=-16777216,e|=f.AttributeData.fromColorRGB([i,s,r])):5===t&&(e&=-50331904,e|=33554432|255&i),e}_extractColor(e,t,i){const s=[0,0,-1,0,0,0];let r=0,n=0;do{if(s[n+r]=e.params[t+n],e.hasSubParams(t+n)){const i=e.getSubParams(t+n);let o=0;do{5===s[1]&&(r=1),s[n+o+1+r]=i[o]}while(++o=2||2===s[1]&&n+r>=5)break;s[1]&&(r=1)}while(++n+t5)&&(e=1),t.extended.underlineStyle=e,t.fg|=268435456,0===e&&(t.fg&=-268435457),t.updateExtended()}_processSGR0(e){e.fg=l.DEFAULT_ATTR_DATA.fg,e.bg=l.DEFAULT_ATTR_DATA.bg,e.extended=e.extended.clone(),e.extended.underlineStyle=0,e.extended.underlineColor&=-67108864,e.updateExtended()}charAttributes(e){if(1===e.length&&0===e.params[0])return this._processSGR0(this._curAttrData),!0;const t=e.length;let i;const s=this._curAttrData;for(let r=0;r=30&&i<=37?(s.fg&=-50331904,s.fg|=16777216|i-30):i>=40&&i<=47?(s.bg&=-50331904,s.bg|=16777216|i-40):i>=90&&i<=97?(s.fg&=-50331904,s.fg|=16777224|i-90):i>=100&&i<=107?(s.bg&=-50331904,s.bg|=16777224|i-100):0===i?this._processSGR0(s):1===i?s.fg|=134217728:3===i?s.bg|=67108864:4===i?(s.fg|=268435456,this._processUnderline(e.hasSubParams(r)?e.getSubParams(r)[0]:1,s)):5===i?s.fg|=536870912:7===i?s.fg|=67108864:8===i?s.fg|=1073741824:9===i?s.fg|=2147483648:2===i?s.bg|=134217728:21===i?this._processUnderline(2,s):22===i?(s.fg&=-134217729,s.bg&=-134217729):23===i?s.bg&=-67108865:24===i?(s.fg&=-268435457,this._processUnderline(0,s)):25===i?s.fg&=-536870913:27===i?s.fg&=-67108865:28===i?s.fg&=-1073741825:29===i?s.fg&=2147483647:39===i?(s.fg&=-67108864,s.fg|=16777215&l.DEFAULT_ATTR_DATA.fg):49===i?(s.bg&=-67108864,s.bg|=16777215&l.DEFAULT_ATTR_DATA.bg):38===i||48===i||58===i?r+=this._extractColor(e,r,s):53===i?s.bg|=1073741824:55===i?s.bg&=-1073741825:59===i?(s.extended=s.extended.clone(),s.extended.underlineColor=-1,s.updateExtended()):100===i?(s.fg&=-67108864,s.fg|=16777215&l.DEFAULT_ATTR_DATA.fg,s.bg&=-67108864,s.bg|=16777215&l.DEFAULT_ATTR_DATA.bg):this._logService.debug("Unknown SGR attribute: %d.",i);return!0}deviceStatus(e){switch(e.params[0]){case 5:this._coreService.triggerDataEvent(`${n.C0.ESC}[0n`);break;case 6:const e=this._activeBuffer.y+1,t=this._activeBuffer.x+1;this._coreService.triggerDataEvent(`${n.C0.ESC}[${e};${t}R`)}return!0}deviceStatusPrivate(e){if(6===e.params[0]){const e=this._activeBuffer.y+1,t=this._activeBuffer.x+1;this._coreService.triggerDataEvent(`${n.C0.ESC}[?${e};${t}R`)}return!0}softReset(e){return this._coreService.isCursorHidden=!1,this._onRequestSyncScrollBar.fire(),this._activeBuffer.scrollTop=0,this._activeBuffer.scrollBottom=this._bufferService.rows-1,this._curAttrData=l.DEFAULT_ATTR_DATA.clone(),this._coreService.reset(),this._charsetService.reset(),this._activeBuffer.savedX=0,this._activeBuffer.savedY=this._activeBuffer.ybase,this._activeBuffer.savedCurAttrData.fg=this._curAttrData.fg,this._activeBuffer.savedCurAttrData.bg=this._curAttrData.bg,this._activeBuffer.savedCharset=this._charsetService.charset,this._coreService.decPrivateModes.origin=!1,!0}setCursorStyle(e){const t=e.params[0]||1;switch(t){case 1:case 2:this._optionsService.options.cursorStyle="block";break;case 3:case 4:this._optionsService.options.cursorStyle="underline";break;case 5:case 6:this._optionsService.options.cursorStyle="bar"}const i=t%2==1;return this._optionsService.options.cursorBlink=i,!0}setScrollRegion(e){const t=e.params[0]||1;let i;return(e.length<2||(i=e.params[1])>this._bufferService.rows||0===i)&&(i=this._bufferService.rows),i>t&&(this._activeBuffer.scrollTop=t-1,this._activeBuffer.scrollBottom=i-1,this._setCursor(0,0)),!0}windowOptions(e){if(!b(e.params[0],this._optionsService.rawOptions.windowOptions))return!0;const t=e.length>1?e.params[1]:0;switch(e.params[0]){case 14:2!==t&&this._onRequestWindowsOptionsReport.fire(y.GET_WIN_SIZE_PIXELS);break;case 16:this._onRequestWindowsOptionsReport.fire(y.GET_CELL_SIZE_PIXELS);break;case 18:this._bufferService&&this._coreService.triggerDataEvent(`${n.C0.ESC}[8;${this._bufferService.rows};${this._bufferService.cols}t`);break;case 22:0!==t&&2!==t||(this._windowTitleStack.push(this._windowTitle),this._windowTitleStack.length>10&&this._windowTitleStack.shift()),0!==t&&1!==t||(this._iconNameStack.push(this._iconName),this._iconNameStack.length>10&&this._iconNameStack.shift());break;case 23:0!==t&&2!==t||this._windowTitleStack.length&&this.setTitle(this._windowTitleStack.pop()),0!==t&&1!==t||this._iconNameStack.length&&this.setIconName(this._iconNameStack.pop())}return!0}saveCursor(e){return this._activeBuffer.savedX=this._activeBuffer.x,this._activeBuffer.savedY=this._activeBuffer.ybase+this._activeBuffer.y,this._activeBuffer.savedCurAttrData.fg=this._curAttrData.fg,this._activeBuffer.savedCurAttrData.bg=this._curAttrData.bg,this._activeBuffer.savedCharset=this._charsetService.charset,!0}restoreCursor(e){return this._activeBuffer.x=this._activeBuffer.savedX||0,this._activeBuffer.y=Math.max(this._activeBuffer.savedY-this._activeBuffer.ybase,0),this._curAttrData.fg=this._activeBuffer.savedCurAttrData.fg,this._curAttrData.bg=this._activeBuffer.savedCurAttrData.bg,this._charsetService.charset=this._savedCharset,this._activeBuffer.savedCharset&&(this._charsetService.charset=this._activeBuffer.savedCharset),this._restrictCursor(),!0}setTitle(e){return this._windowTitle=e,this._onTitleChange.fire(e),!0}setIconName(e){return this._iconName=e,!0}setOrReportIndexedColor(e){const t=[],i=e.split(";");for(;i.length>1;){const e=i.shift(),s=i.shift();if(/^\d+$/.exec(e)){const i=parseInt(e);if(L(i))if("?"===s)t.push({type:0,index:i});else{const e=(0,m.parseColor)(s);e&&t.push({type:1,index:i,color:e})}}}return t.length&&this._onColor.fire(t),!0}setHyperlink(e){const t=e.split(";");return!(t.length<2)&&(t[1]?this._createHyperlink(t[0],t[1]):!t[0]&&this._finishHyperlink())}_createHyperlink(e,t){this._getCurrentLinkId()&&this._finishHyperlink();const i=e.split(":");let s;const r=i.findIndex((e=>e.startsWith("id=")));return-1!==r&&(s=i[r].slice(3)||void 0),this._curAttrData.extended=this._curAttrData.extended.clone(),this._curAttrData.extended.urlId=this._oscLinkService.registerLink({id:s,uri:t}),this._curAttrData.updateExtended(),!0}_finishHyperlink(){return this._curAttrData.extended=this._curAttrData.extended.clone(),this._curAttrData.extended.urlId=0,this._curAttrData.updateExtended(),!0}_setOrReportSpecialColor(e,t){const i=e.split(";");for(let e=0;e=this._specialColors.length);++e,++t)if("?"===i[e])this._onColor.fire([{type:0,index:this._specialColors[t]}]);else{const s=(0,m.parseColor)(i[e]);s&&this._onColor.fire([{type:1,index:this._specialColors[t],color:s}])}return!0}setOrReportFgColor(e){return this._setOrReportSpecialColor(e,0)}setOrReportBgColor(e){return this._setOrReportSpecialColor(e,1)}setOrReportCursorColor(e){return this._setOrReportSpecialColor(e,2)}restoreIndexedColor(e){if(!e)return this._onColor.fire([{type:2}]),!0;const t=[],i=e.split(";");for(let e=0;e=this._bufferService.rows&&(this._activeBuffer.y=this._bufferService.rows-1),this._restrictCursor(),!0}tabSet(){return this._activeBuffer.tabs[this._activeBuffer.x]=!0,!0}reverseIndex(){if(this._restrictCursor(),this._activeBuffer.y===this._activeBuffer.scrollTop){const e=this._activeBuffer.scrollBottom-this._activeBuffer.scrollTop;this._activeBuffer.lines.shiftElements(this._activeBuffer.ybase+this._activeBuffer.y,e,1),this._activeBuffer.lines.set(this._activeBuffer.ybase+this._activeBuffer.y,this._activeBuffer.getBlankLine(this._eraseAttrData())),this._dirtyRowTracker.markRangeDirty(this._activeBuffer.scrollTop,this._activeBuffer.scrollBottom)}else this._activeBuffer.y--,this._restrictCursor();return!0}fullReset(){return this._parser.reset(),this._onRequestReset.fire(),!0}reset(){this._curAttrData=l.DEFAULT_ATTR_DATA.clone(),this._eraseAttrDataInternal=l.DEFAULT_ATTR_DATA.clone()}_eraseAttrData(){return this._eraseAttrDataInternal.bg&=-67108864,this._eraseAttrDataInternal.bg|=67108863&this._curAttrData.bg,this._eraseAttrDataInternal}setgLevel(e){return this._charsetService.setgLevel(e),!0}screenAlignmentPattern(){const e=new u.CellData;e.content=1<<22|"E".charCodeAt(0),e.fg=this._curAttrData.fg,e.bg=this._curAttrData.bg,this._setCursor(0,0);for(let t=0;t(this._coreService.triggerDataEvent(`${n.C0.ESC}${e}${n.C0.ESC}\\`),!0))('"q'===e?`P1$r${this._curAttrData.isProtected()?1:0}"q`:'"p'===e?'P1$r61;1"p':"r"===e?`P1$r${i.scrollTop+1};${i.scrollBottom+1}r`:"m"===e?"P1$r0m":" q"===e?`P1$r${{block:2,underline:4,bar:6}[s.cursorStyle]-(s.cursorBlink?1:0)} q`:"P0$r")}markRangeDirty(e,t){this._dirtyRowTracker.markRangeDirty(e,t)}}t.InputHandler=E;let k=class{constructor(e){this._bufferService=e,this.clearRange()}clearRange(){this.start=this._bufferService.buffer.y,this.end=this._bufferService.buffer.y}markDirty(e){ethis.end&&(this.end=e)}markRangeDirty(e,t){e>t&&(w=e,e=t,t=w),ethis.end&&(this.end=t)}markAllDirty(){this.markRangeDirty(0,this._bufferService.rows-1)}};function L(e){return 0<=e&&e<256}k=s([r(0,v.IBufferService)],k)},844:(e,t)=>{function i(e){for(const t of e)t.dispose();e.length=0}Object.defineProperty(t,"__esModule",{value:!0}),t.getDisposeArrayDisposable=t.disposeArray=t.toDisposable=t.MutableDisposable=t.Disposable=void 0,t.Disposable=class{constructor(){this._disposables=[],this._isDisposed=!1}dispose(){this._isDisposed=!0;for(const e of this._disposables)e.dispose();this._disposables.length=0}register(e){return this._disposables.push(e),e}unregister(e){const t=this._disposables.indexOf(e);-1!==t&&this._disposables.splice(t,1)}},t.MutableDisposable=class{constructor(){this._isDisposed=!1}get value(){return this._isDisposed?void 0:this._value}set value(e){var t;this._isDisposed||e===this._value||(null===(t=this._value)||void 0===t||t.dispose(),this._value=e)}clear(){this.value=void 0}dispose(){var e;this._isDisposed=!0,null===(e=this._value)||void 0===e||e.dispose(),this._value=void 0}},t.toDisposable=function(e){return{dispose:e}},t.disposeArray=i,t.getDisposeArrayDisposable=function(e){return{dispose:()=>i(e)}}},1505:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.FourKeyMap=t.TwoKeyMap=void 0;class i{constructor(){this._data={}}set(e,t,i){this._data[e]||(this._data[e]={}),this._data[e][t]=i}get(e,t){return this._data[e]?this._data[e][t]:void 0}clear(){this._data={}}}t.TwoKeyMap=i,t.FourKeyMap=class{constructor(){this._data=new i}set(e,t,s,r,n){this._data.get(e,t)||this._data.set(e,t,new i),this._data.get(e,t).set(s,r,n)}get(e,t,i,s){var r;return null===(r=this._data.get(e,t))||void 0===r?void 0:r.get(i,s)}clear(){this._data.clear()}}},6114:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.isChromeOS=t.isLinux=t.isWindows=t.isIphone=t.isIpad=t.isMac=t.getSafariVersion=t.isSafari=t.isLegacyEdge=t.isFirefox=t.isNode=void 0,t.isNode="undefined"==typeof navigator;const i=t.isNode?"node":navigator.userAgent,s=t.isNode?"node":navigator.platform;t.isFirefox=i.includes("Firefox"),t.isLegacyEdge=i.includes("Edge"),t.isSafari=/^((?!chrome|android).)*safari/i.test(i),t.getSafariVersion=function(){if(!t.isSafari)return 0;const e=i.match(/Version\/(\d+)/);return null===e||e.length<2?0:parseInt(e[1])},t.isMac=["Macintosh","MacIntel","MacPPC","Mac68K"].includes(s),t.isIpad="iPad"===s,t.isIphone="iPhone"===s,t.isWindows=["Windows","Win16","Win32","WinCE"].includes(s),t.isLinux=s.indexOf("Linux")>=0,t.isChromeOS=/\bCrOS\b/.test(i)},6106:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.SortedList=void 0;let i=0;t.SortedList=class{constructor(e){this._getKey=e,this._array=[]}clear(){this._array.length=0}insert(e){0!==this._array.length?(i=this._search(this._getKey(e)),this._array.splice(i,0,e)):this._array.push(e)}delete(e){if(0===this._array.length)return!1;const t=this._getKey(e);if(void 0===t)return!1;if(i=this._search(t),-1===i)return!1;if(this._getKey(this._array[i])!==t)return!1;do{if(this._array[i]===e)return this._array.splice(i,1),!0}while(++i=this._array.length)&&this._getKey(this._array[i])===e))do{yield this._array[i]}while(++i=this._array.length)&&this._getKey(this._array[i])===e))do{t(this._array[i])}while(++i=t;){let s=t+i>>1;const r=this._getKey(this._array[s]);if(r>e)i=s-1;else{if(!(r0&&this._getKey(this._array[s-1])===e;)s--;return s}t=s+1}}return t}}},7226:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.DebouncedIdleTask=t.IdleTaskQueue=t.PriorityTaskQueue=void 0;const s=i(6114);class r{constructor(){this._tasks=[],this._i=0}enqueue(e){this._tasks.push(e),this._start()}flush(){for(;this._ir)return s-t<-20&&console.warn(`task queue exceeded allotted deadline by ${Math.abs(Math.round(s-t))}ms`),void this._start();s=r}this.clear()}}class n extends r{_requestCallback(e){return setTimeout((()=>e(this._createDeadline(16))))}_cancelCallback(e){clearTimeout(e)}_createDeadline(e){const t=Date.now()+e;return{timeRemaining:()=>Math.max(0,t-Date.now())}}}t.PriorityTaskQueue=n,t.IdleTaskQueue=!s.isNode&&"requestIdleCallback"in window?class extends r{_requestCallback(e){return requestIdleCallback(e)}_cancelCallback(e){cancelIdleCallback(e)}}:n,t.DebouncedIdleTask=class{constructor(){this._queue=new t.IdleTaskQueue}set(e){this._queue.clear(),this._queue.enqueue(e)}flush(){this._queue.flush()}}},9282:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.updateWindowsModeWrappedState=void 0;const s=i(643);t.updateWindowsModeWrappedState=function(e){const t=e.buffer.lines.get(e.buffer.ybase+e.buffer.y-1),i=null==t?void 0:t.get(e.cols-1),r=e.buffer.lines.get(e.buffer.ybase+e.buffer.y);r&&i&&(r.isWrapped=i[s.CHAR_DATA_CODE_INDEX]!==s.NULL_CELL_CODE&&i[s.CHAR_DATA_CODE_INDEX]!==s.WHITESPACE_CELL_CODE)}},3734:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.ExtendedAttrs=t.AttributeData=void 0;class i{constructor(){this.fg=0,this.bg=0,this.extended=new s}static toColorRGB(e){return[e>>>16&255,e>>>8&255,255&e]}static fromColorRGB(e){return(255&e[0])<<16|(255&e[1])<<8|255&e[2]}clone(){const e=new i;return e.fg=this.fg,e.bg=this.bg,e.extended=this.extended.clone(),e}isInverse(){return 67108864&this.fg}isBold(){return 134217728&this.fg}isUnderline(){return this.hasExtendedAttrs()&&0!==this.extended.underlineStyle?1:268435456&this.fg}isBlink(){return 536870912&this.fg}isInvisible(){return 1073741824&this.fg}isItalic(){return 67108864&this.bg}isDim(){return 134217728&this.bg}isStrikethrough(){return 2147483648&this.fg}isProtected(){return 536870912&this.bg}isOverline(){return 1073741824&this.bg}getFgColorMode(){return 50331648&this.fg}getBgColorMode(){return 50331648&this.bg}isFgRGB(){return 50331648==(50331648&this.fg)}isBgRGB(){return 50331648==(50331648&this.bg)}isFgPalette(){return 16777216==(50331648&this.fg)||33554432==(50331648&this.fg)}isBgPalette(){return 16777216==(50331648&this.bg)||33554432==(50331648&this.bg)}isFgDefault(){return 0==(50331648&this.fg)}isBgDefault(){return 0==(50331648&this.bg)}isAttributeDefault(){return 0===this.fg&&0===this.bg}getFgColor(){switch(50331648&this.fg){case 16777216:case 33554432:return 255&this.fg;case 50331648:return 16777215&this.fg;default:return-1}}getBgColor(){switch(50331648&this.bg){case 16777216:case 33554432:return 255&this.bg;case 50331648:return 16777215&this.bg;default:return-1}}hasExtendedAttrs(){return 268435456&this.bg}updateExtended(){this.extended.isEmpty()?this.bg&=-268435457:this.bg|=268435456}getUnderlineColor(){if(268435456&this.bg&&~this.extended.underlineColor)switch(50331648&this.extended.underlineColor){case 16777216:case 33554432:return 255&this.extended.underlineColor;case 50331648:return 16777215&this.extended.underlineColor;default:return this.getFgColor()}return this.getFgColor()}getUnderlineColorMode(){return 268435456&this.bg&&~this.extended.underlineColor?50331648&this.extended.underlineColor:this.getFgColorMode()}isUnderlineColorRGB(){return 268435456&this.bg&&~this.extended.underlineColor?50331648==(50331648&this.extended.underlineColor):this.isFgRGB()}isUnderlineColorPalette(){return 268435456&this.bg&&~this.extended.underlineColor?16777216==(50331648&this.extended.underlineColor)||33554432==(50331648&this.extended.underlineColor):this.isFgPalette()}isUnderlineColorDefault(){return 268435456&this.bg&&~this.extended.underlineColor?0==(50331648&this.extended.underlineColor):this.isFgDefault()}getUnderlineStyle(){return 268435456&this.fg?268435456&this.bg?this.extended.underlineStyle:1:0}}t.AttributeData=i;class s{get ext(){return this._urlId?-469762049&this._ext|this.underlineStyle<<26:this._ext}set ext(e){this._ext=e}get underlineStyle(){return this._urlId?5:(469762048&this._ext)>>26}set underlineStyle(e){this._ext&=-469762049,this._ext|=e<<26&469762048}get underlineColor(){return 67108863&this._ext}set underlineColor(e){this._ext&=-67108864,this._ext|=67108863&e}get urlId(){return this._urlId}set urlId(e){this._urlId=e}constructor(e=0,t=0){this._ext=0,this._urlId=0,this._ext=e,this._urlId=t}clone(){return new s(this._ext,this._urlId)}isEmpty(){return 0===this.underlineStyle&&0===this._urlId}}t.ExtendedAttrs=s},9092:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.Buffer=t.MAX_BUFFER_SIZE=void 0;const s=i(6349),r=i(7226),n=i(3734),o=i(8437),a=i(4634),h=i(511),c=i(643),l=i(4863),d=i(7116);t.MAX_BUFFER_SIZE=4294967295,t.Buffer=class{constructor(e,t,i){this._hasScrollback=e,this._optionsService=t,this._bufferService=i,this.ydisp=0,this.ybase=0,this.y=0,this.x=0,this.tabs={},this.savedY=0,this.savedX=0,this.savedCurAttrData=o.DEFAULT_ATTR_DATA.clone(),this.savedCharset=d.DEFAULT_CHARSET,this.markers=[],this._nullCell=h.CellData.fromCharData([0,c.NULL_CELL_CHAR,c.NULL_CELL_WIDTH,c.NULL_CELL_CODE]),this._whitespaceCell=h.CellData.fromCharData([0,c.WHITESPACE_CELL_CHAR,c.WHITESPACE_CELL_WIDTH,c.WHITESPACE_CELL_CODE]),this._isClearing=!1,this._memoryCleanupQueue=new r.IdleTaskQueue,this._memoryCleanupPosition=0,this._cols=this._bufferService.cols,this._rows=this._bufferService.rows,this.lines=new s.CircularList(this._getCorrectBufferLength(this._rows)),this.scrollTop=0,this.scrollBottom=this._rows-1,this.setupTabStops()}getNullCell(e){return e?(this._nullCell.fg=e.fg,this._nullCell.bg=e.bg,this._nullCell.extended=e.extended):(this._nullCell.fg=0,this._nullCell.bg=0,this._nullCell.extended=new n.ExtendedAttrs),this._nullCell}getWhitespaceCell(e){return e?(this._whitespaceCell.fg=e.fg,this._whitespaceCell.bg=e.bg,this._whitespaceCell.extended=e.extended):(this._whitespaceCell.fg=0,this._whitespaceCell.bg=0,this._whitespaceCell.extended=new n.ExtendedAttrs),this._whitespaceCell}getBlankLine(e,t){return new o.BufferLine(this._bufferService.cols,this.getNullCell(e),t)}get hasScrollback(){return this._hasScrollback&&this.lines.maxLength>this._rows}get isCursorInViewport(){const e=this.ybase+this.y-this.ydisp;return e>=0&&et.MAX_BUFFER_SIZE?t.MAX_BUFFER_SIZE:i}fillViewportRows(e){if(0===this.lines.length){void 0===e&&(e=o.DEFAULT_ATTR_DATA);let t=this._rows;for(;t--;)this.lines.push(this.getBlankLine(e))}}clear(){this.ydisp=0,this.ybase=0,this.y=0,this.x=0,this.lines=new s.CircularList(this._getCorrectBufferLength(this._rows)),this.scrollTop=0,this.scrollBottom=this._rows-1,this.setupTabStops()}resize(e,t){const i=this.getNullCell(o.DEFAULT_ATTR_DATA);let s=0;const r=this._getCorrectBufferLength(t);if(r>this.lines.maxLength&&(this.lines.maxLength=r),this.lines.length>0){if(this._cols0&&this.lines.length<=this.ybase+this.y+n+1?(this.ybase--,n++,this.ydisp>0&&this.ydisp--):this.lines.push(new o.BufferLine(e,i)));else for(let e=this._rows;e>t;e--)this.lines.length>t+this.ybase&&(this.lines.length>this.ybase+this.y+1?this.lines.pop():(this.ybase++,this.ydisp++));if(r0&&(this.lines.trimStart(e),this.ybase=Math.max(this.ybase-e,0),this.ydisp=Math.max(this.ydisp-e,0),this.savedY=Math.max(this.savedY-e,0)),this.lines.maxLength=r}this.x=Math.min(this.x,e-1),this.y=Math.min(this.y,t-1),n&&(this.y+=n),this.savedX=Math.min(this.savedX,e-1),this.scrollTop=0}if(this.scrollBottom=t-1,this._isReflowEnabled&&(this._reflow(e,t),this._cols>e))for(let t=0;t.1*this.lines.length&&(this._memoryCleanupPosition=0,this._memoryCleanupQueue.enqueue((()=>this._batchedMemoryCleanup())))}_batchedMemoryCleanup(){let e=!0;this._memoryCleanupPosition>=this.lines.length&&(this._memoryCleanupPosition=0,e=!1);let t=0;for(;this._memoryCleanupPosition100)return!0;return e}get _isReflowEnabled(){const e=this._optionsService.rawOptions.windowsPty;return e&&e.buildNumber?this._hasScrollback&&"conpty"===e.backend&&e.buildNumber>=21376:this._hasScrollback&&!this._optionsService.rawOptions.windowsMode}_reflow(e,t){this._cols!==e&&(e>this._cols?this._reflowLarger(e,t):this._reflowSmaller(e,t))}_reflowLarger(e,t){const i=(0,a.reflowLargerGetLinesToRemove)(this.lines,this._cols,e,this.ybase+this.y,this.getNullCell(o.DEFAULT_ATTR_DATA));if(i.length>0){const s=(0,a.reflowLargerCreateNewLayout)(this.lines,i);(0,a.reflowLargerApplyNewLayout)(this.lines,s.layout),this._reflowLargerAdjustViewport(e,t,s.countRemoved)}}_reflowLargerAdjustViewport(e,t,i){const s=this.getNullCell(o.DEFAULT_ATTR_DATA);let r=i;for(;r-- >0;)0===this.ybase?(this.y>0&&this.y--,this.lines.length=0;n--){let h=this.lines.get(n);if(!h||!h.isWrapped&&h.getTrimmedLength()<=e)continue;const c=[h];for(;h.isWrapped&&n>0;)h=this.lines.get(--n),c.unshift(h);const l=this.ybase+this.y;if(l>=n&&l0&&(s.push({start:n+c.length+r,newLines:v}),r+=v.length),c.push(...v);let p=_.length-1,g=_[p];0===g&&(p--,g=_[p]);let m=c.length-u-1,S=d;for(;m>=0;){const e=Math.min(S,g);if(void 0===c[p])break;if(c[p].copyCellsFrom(c[m],S-e,g-e,e,!0),g-=e,0===g&&(p--,g=_[p]),S-=e,0===S){m--;const e=Math.max(m,0);S=(0,a.getWrappedLineTrimmedLength)(c,e,this._cols)}}for(let t=0;t0;)0===this.ybase?this.y0){const e=[],t=[];for(let e=0;e=0;c--)if(a&&a.start>n+h){for(let e=a.newLines.length-1;e>=0;e--)this.lines.set(c--,a.newLines[e]);c++,e.push({index:n+1,amount:a.newLines.length}),h+=a.newLines.length,a=s[++o]}else this.lines.set(c,t[n--]);let c=0;for(let t=e.length-1;t>=0;t--)e[t].index+=c,this.lines.onInsertEmitter.fire(e[t]),c+=e[t].amount;const l=Math.max(0,i+r-this.lines.maxLength);l>0&&this.lines.onTrimEmitter.fire(l)}}translateBufferLineToString(e,t,i=0,s){const r=this.lines.get(e);return r?r.translateToString(t,i,s):""}getWrappedRangeForLine(e){let t=e,i=e;for(;t>0&&this.lines.get(t).isWrapped;)t--;for(;i+10;);return e>=this._cols?this._cols-1:e<0?0:e}nextStop(e){for(null==e&&(e=this.x);!this.tabs[++e]&&e=this._cols?this._cols-1:e<0?0:e}clearMarkers(e){this._isClearing=!0;for(let t=0;t{t.line-=e,t.line<0&&t.dispose()}))),t.register(this.lines.onInsert((e=>{t.line>=e.index&&(t.line+=e.amount)}))),t.register(this.lines.onDelete((e=>{t.line>=e.index&&t.linee.index&&(t.line-=e.amount)}))),t.register(t.onDispose((()=>this._removeMarker(t)))),t}_removeMarker(e){this._isClearing||this.markers.splice(this.markers.indexOf(e),1)}}},8437:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.BufferLine=t.DEFAULT_ATTR_DATA=void 0;const s=i(3734),r=i(511),n=i(643),o=i(482);t.DEFAULT_ATTR_DATA=Object.freeze(new s.AttributeData);let a=0;class h{constructor(e,t,i=!1){this.isWrapped=i,this._combined={},this._extendedAttrs={},this._data=new Uint32Array(3*e);const s=t||r.CellData.fromCharData([0,n.NULL_CELL_CHAR,n.NULL_CELL_WIDTH,n.NULL_CELL_CODE]);for(let t=0;t>22,2097152&t?this._combined[e].charCodeAt(this._combined[e].length-1):i]}set(e,t){this._data[3*e+1]=t[n.CHAR_DATA_ATTR_INDEX],t[n.CHAR_DATA_CHAR_INDEX].length>1?(this._combined[e]=t[1],this._data[3*e+0]=2097152|e|t[n.CHAR_DATA_WIDTH_INDEX]<<22):this._data[3*e+0]=t[n.CHAR_DATA_CHAR_INDEX].charCodeAt(0)|t[n.CHAR_DATA_WIDTH_INDEX]<<22}getWidth(e){return this._data[3*e+0]>>22}hasWidth(e){return 12582912&this._data[3*e+0]}getFg(e){return this._data[3*e+1]}getBg(e){return this._data[3*e+2]}hasContent(e){return 4194303&this._data[3*e+0]}getCodePoint(e){const t=this._data[3*e+0];return 2097152&t?this._combined[e].charCodeAt(this._combined[e].length-1):2097151&t}isCombined(e){return 2097152&this._data[3*e+0]}getString(e){const t=this._data[3*e+0];return 2097152&t?this._combined[e]:2097151&t?(0,o.stringFromCodePoint)(2097151&t):""}isProtected(e){return 536870912&this._data[3*e+2]}loadCell(e,t){return a=3*e,t.content=this._data[a+0],t.fg=this._data[a+1],t.bg=this._data[a+2],2097152&t.content&&(t.combinedData=this._combined[e]),268435456&t.bg&&(t.extended=this._extendedAttrs[e]),t}setCell(e,t){2097152&t.content&&(this._combined[e]=t.combinedData),268435456&t.bg&&(this._extendedAttrs[e]=t.extended),this._data[3*e+0]=t.content,this._data[3*e+1]=t.fg,this._data[3*e+2]=t.bg}setCellFromCodePoint(e,t,i,s,r,n){268435456&r&&(this._extendedAttrs[e]=n),this._data[3*e+0]=t|i<<22,this._data[3*e+1]=s,this._data[3*e+2]=r}addCodepointToCell(e,t){let i=this._data[3*e+0];2097152&i?this._combined[e]+=(0,o.stringFromCodePoint)(t):(2097151&i?(this._combined[e]=(0,o.stringFromCodePoint)(2097151&i)+(0,o.stringFromCodePoint)(t),i&=-2097152,i|=2097152):i=t|1<<22,this._data[3*e+0]=i)}insertCells(e,t,i,n){if((e%=this.length)&&2===this.getWidth(e-1)&&this.setCellFromCodePoint(e-1,0,1,(null==n?void 0:n.fg)||0,(null==n?void 0:n.bg)||0,(null==n?void 0:n.extended)||new s.ExtendedAttrs),t=0;--i)this.setCell(e+t+i,this.loadCell(e+i,s));for(let s=0;sthis.length){if(this._data.buffer.byteLength>=4*i)this._data=new Uint32Array(this._data.buffer,0,i);else{const e=new Uint32Array(i);e.set(this._data),this._data=e}for(let i=this.length;i=e&&delete this._combined[s]}const s=Object.keys(this._extendedAttrs);for(let t=0;t=e&&delete this._extendedAttrs[i]}}return this.length=e,4*i*2=0;--e)if(4194303&this._data[3*e+0])return e+(this._data[3*e+0]>>22);return 0}getNoBgTrimmedLength(){for(let e=this.length-1;e>=0;--e)if(4194303&this._data[3*e+0]||50331648&this._data[3*e+2])return e+(this._data[3*e+0]>>22);return 0}copyCellsFrom(e,t,i,s,r){const n=e._data;if(r)for(let r=s-1;r>=0;r--){for(let e=0;e<3;e++)this._data[3*(i+r)+e]=n[3*(t+r)+e];268435456&n[3*(t+r)+2]&&(this._extendedAttrs[i+r]=e._extendedAttrs[t+r])}else for(let r=0;r=t&&(this._combined[r-t+i]=e._combined[r])}}translateToString(e=!1,t=0,i=this.length){e&&(i=Math.min(i,this.getTrimmedLength()));let s="";for(;t>22||1}return s}}t.BufferLine=h},4841:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.getRangeLength=void 0,t.getRangeLength=function(e,t){if(e.start.y>e.end.y)throw new Error(`Buffer range end (${e.end.x}, ${e.end.y}) cannot be before start (${e.start.x}, ${e.start.y})`);return t*(e.end.y-e.start.y)+(e.end.x-e.start.x+1)}},4634:(e,t)=>{function i(e,t,i){if(t===e.length-1)return e[t].getTrimmedLength();const s=!e[t].hasContent(i-1)&&1===e[t].getWidth(i-1),r=2===e[t+1].getWidth(0);return s&&r?i-1:i}Object.defineProperty(t,"__esModule",{value:!0}),t.getWrappedLineTrimmedLength=t.reflowSmallerGetNewLineLengths=t.reflowLargerApplyNewLayout=t.reflowLargerCreateNewLayout=t.reflowLargerGetLinesToRemove=void 0,t.reflowLargerGetLinesToRemove=function(e,t,s,r,n){const o=[];for(let a=0;a=a&&r0&&(e>d||0===l[e].getTrimmedLength());e--)v++;v>0&&(o.push(a+l.length-v),o.push(v)),a+=l.length-1}return o},t.reflowLargerCreateNewLayout=function(e,t){const i=[];let s=0,r=t[s],n=0;for(let o=0;oi(e,r,t))).reduce(((e,t)=>e+t));let o=0,a=0,h=0;for(;hc&&(o-=c,a++);const l=2===e[a].getWidth(o-1);l&&o--;const d=l?s-1:s;r.push(d),h+=d}return r},t.getWrappedLineTrimmedLength=i},5295:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.BufferSet=void 0;const s=i(8460),r=i(844),n=i(9092);class o extends r.Disposable{constructor(e,t){super(),this._optionsService=e,this._bufferService=t,this._onBufferActivate=this.register(new s.EventEmitter),this.onBufferActivate=this._onBufferActivate.event,this.reset(),this.register(this._optionsService.onSpecificOptionChange("scrollback",(()=>this.resize(this._bufferService.cols,this._bufferService.rows)))),this.register(this._optionsService.onSpecificOptionChange("tabStopWidth",(()=>this.setupTabStops())))}reset(){this._normal=new n.Buffer(!0,this._optionsService,this._bufferService),this._normal.fillViewportRows(),this._alt=new n.Buffer(!1,this._optionsService,this._bufferService),this._activeBuffer=this._normal,this._onBufferActivate.fire({activeBuffer:this._normal,inactiveBuffer:this._alt}),this.setupTabStops()}get alt(){return this._alt}get active(){return this._activeBuffer}get normal(){return this._normal}activateNormalBuffer(){this._activeBuffer!==this._normal&&(this._normal.x=this._alt.x,this._normal.y=this._alt.y,this._alt.clearAllMarkers(),this._alt.clear(),this._activeBuffer=this._normal,this._onBufferActivate.fire({activeBuffer:this._normal,inactiveBuffer:this._alt}))}activateAltBuffer(e){this._activeBuffer!==this._alt&&(this._alt.fillViewportRows(e),this._alt.x=this._normal.x,this._alt.y=this._normal.y,this._activeBuffer=this._alt,this._onBufferActivate.fire({activeBuffer:this._alt,inactiveBuffer:this._normal}))}resize(e,t){this._normal.resize(e,t),this._alt.resize(e,t),this.setupTabStops(e)}setupTabStops(e){this._normal.setupTabStops(e),this._alt.setupTabStops(e)}}t.BufferSet=o},511:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.CellData=void 0;const s=i(482),r=i(643),n=i(3734);class o extends n.AttributeData{constructor(){super(...arguments),this.content=0,this.fg=0,this.bg=0,this.extended=new n.ExtendedAttrs,this.combinedData=""}static fromCharData(e){const t=new o;return t.setFromCharData(e),t}isCombined(){return 2097152&this.content}getWidth(){return this.content>>22}getChars(){return 2097152&this.content?this.combinedData:2097151&this.content?(0,s.stringFromCodePoint)(2097151&this.content):""}getCode(){return this.isCombined()?this.combinedData.charCodeAt(this.combinedData.length-1):2097151&this.content}setFromCharData(e){this.fg=e[r.CHAR_DATA_ATTR_INDEX],this.bg=0;let t=!1;if(e[r.CHAR_DATA_CHAR_INDEX].length>2)t=!0;else if(2===e[r.CHAR_DATA_CHAR_INDEX].length){const i=e[r.CHAR_DATA_CHAR_INDEX].charCodeAt(0);if(55296<=i&&i<=56319){const s=e[r.CHAR_DATA_CHAR_INDEX].charCodeAt(1);56320<=s&&s<=57343?this.content=1024*(i-55296)+s-56320+65536|e[r.CHAR_DATA_WIDTH_INDEX]<<22:t=!0}else t=!0}else this.content=e[r.CHAR_DATA_CHAR_INDEX].charCodeAt(0)|e[r.CHAR_DATA_WIDTH_INDEX]<<22;t&&(this.combinedData=e[r.CHAR_DATA_CHAR_INDEX],this.content=2097152|e[r.CHAR_DATA_WIDTH_INDEX]<<22)}getAsCharData(){return[this.fg,this.getChars(),this.getWidth(),this.getCode()]}}t.CellData=o},643:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.WHITESPACE_CELL_CODE=t.WHITESPACE_CELL_WIDTH=t.WHITESPACE_CELL_CHAR=t.NULL_CELL_CODE=t.NULL_CELL_WIDTH=t.NULL_CELL_CHAR=t.CHAR_DATA_CODE_INDEX=t.CHAR_DATA_WIDTH_INDEX=t.CHAR_DATA_CHAR_INDEX=t.CHAR_DATA_ATTR_INDEX=t.DEFAULT_EXT=t.DEFAULT_ATTR=t.DEFAULT_COLOR=void 0,t.DEFAULT_COLOR=0,t.DEFAULT_ATTR=256|t.DEFAULT_COLOR<<9,t.DEFAULT_EXT=0,t.CHAR_DATA_ATTR_INDEX=0,t.CHAR_DATA_CHAR_INDEX=1,t.CHAR_DATA_WIDTH_INDEX=2,t.CHAR_DATA_CODE_INDEX=3,t.NULL_CELL_CHAR="",t.NULL_CELL_WIDTH=1,t.NULL_CELL_CODE=0,t.WHITESPACE_CELL_CHAR=" ",t.WHITESPACE_CELL_WIDTH=1,t.WHITESPACE_CELL_CODE=32},4863:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.Marker=void 0;const s=i(8460),r=i(844);class n{get id(){return this._id}constructor(e){this.line=e,this.isDisposed=!1,this._disposables=[],this._id=n._nextId++,this._onDispose=this.register(new s.EventEmitter),this.onDispose=this._onDispose.event}dispose(){this.isDisposed||(this.isDisposed=!0,this.line=-1,this._onDispose.fire(),(0,r.disposeArray)(this._disposables),this._disposables.length=0)}register(e){return this._disposables.push(e),e}}t.Marker=n,n._nextId=1},7116:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.DEFAULT_CHARSET=t.CHARSETS=void 0,t.CHARSETS={},t.DEFAULT_CHARSET=t.CHARSETS.B,t.CHARSETS[0]={"`":"◆",a:"▒",b:"␉",c:"␌",d:"␍",e:"␊",f:"°",g:"±",h:"␤",i:"␋",j:"┘",k:"┐",l:"┌",m:"└",n:"┼",o:"⎺",p:"⎻",q:"─",r:"⎼",s:"⎽",t:"├",u:"┤",v:"┴",w:"┬",x:"│",y:"≤",z:"≥","{":"π","|":"≠","}":"£","~":"·"},t.CHARSETS.A={"#":"£"},t.CHARSETS.B=void 0,t.CHARSETS[4]={"#":"£","@":"¾","[":"ij","\\":"½","]":"|","{":"¨","|":"f","}":"¼","~":"´"},t.CHARSETS.C=t.CHARSETS[5]={"[":"Ä","\\":"Ö","]":"Å","^":"Ü","`":"é","{":"ä","|":"ö","}":"å","~":"ü"},t.CHARSETS.R={"#":"£","@":"à","[":"°","\\":"ç","]":"§","{":"é","|":"ù","}":"è","~":"¨"},t.CHARSETS.Q={"@":"à","[":"â","\\":"ç","]":"ê","^":"î","`":"ô","{":"é","|":"ù","}":"è","~":"û"},t.CHARSETS.K={"@":"§","[":"Ä","\\":"Ö","]":"Ü","{":"ä","|":"ö","}":"ü","~":"ß"},t.CHARSETS.Y={"#":"£","@":"§","[":"°","\\":"ç","]":"é","`":"ù","{":"à","|":"ò","}":"è","~":"ì"},t.CHARSETS.E=t.CHARSETS[6]={"@":"Ä","[":"Æ","\\":"Ø","]":"Å","^":"Ü","`":"ä","{":"æ","|":"ø","}":"å","~":"ü"},t.CHARSETS.Z={"#":"£","@":"§","[":"¡","\\":"Ñ","]":"¿","{":"°","|":"ñ","}":"ç"},t.CHARSETS.H=t.CHARSETS[7]={"@":"É","[":"Ä","\\":"Ö","]":"Å","^":"Ü","`":"é","{":"ä","|":"ö","}":"å","~":"ü"},t.CHARSETS["="]={"#":"ù","@":"à","[":"é","\\":"ç","]":"ê","^":"î",_:"è","`":"ô","{":"ä","|":"ö","}":"ü","~":"û"}},2584:(e,t)=>{var i,s,r;Object.defineProperty(t,"__esModule",{value:!0}),t.C1_ESCAPED=t.C1=t.C0=void 0,function(e){e.NUL="\0",e.SOH="",e.STX="",e.ETX="",e.EOT="",e.ENQ="",e.ACK="",e.BEL="",e.BS="\b",e.HT="\t",e.LF="\n",e.VT="\v",e.FF="\f",e.CR="\r",e.SO="",e.SI="",e.DLE="",e.DC1="",e.DC2="",e.DC3="",e.DC4="",e.NAK="",e.SYN="",e.ETB="",e.CAN="",e.EM="",e.SUB="",e.ESC="",e.FS="",e.GS="",e.RS="",e.US="",e.SP=" ",e.DEL=""}(i||(t.C0=i={})),function(e){e.PAD="€",e.HOP="",e.BPH="‚",e.NBH="ƒ",e.IND="„",e.NEL="…",e.SSA="†",e.ESA="‡",e.HTS="ˆ",e.HTJ="‰",e.VTS="Š",e.PLD="‹",e.PLU="Œ",e.RI="",e.SS2="Ž",e.SS3="",e.DCS="",e.PU1="‘",e.PU2="’",e.STS="“",e.CCH="”",e.MW="•",e.SPA="–",e.EPA="—",e.SOS="˜",e.SGCI="™",e.SCI="š",e.CSI="›",e.ST="œ",e.OSC="",e.PM="ž",e.APC="Ÿ"}(s||(t.C1=s={})),function(e){e.ST=`${i.ESC}\\`}(r||(t.C1_ESCAPED=r={}))},7399:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.evaluateKeyboardEvent=void 0;const s=i(2584),r={48:["0",")"],49:["1","!"],50:["2","@"],51:["3","#"],52:["4","$"],53:["5","%"],54:["6","^"],55:["7","&"],56:["8","*"],57:["9","("],186:[";",":"],187:["=","+"],188:[",","<"],189:["-","_"],190:[".",">"],191:["/","?"],192:["`","~"],219:["[","{"],220:["\\","|"],221:["]","}"],222:["'",'"']};t.evaluateKeyboardEvent=function(e,t,i,n){const o={type:0,cancel:!1,key:void 0},a=(e.shiftKey?1:0)|(e.altKey?2:0)|(e.ctrlKey?4:0)|(e.metaKey?8:0);switch(e.keyCode){case 0:"UIKeyInputUpArrow"===e.key?o.key=t?s.C0.ESC+"OA":s.C0.ESC+"[A":"UIKeyInputLeftArrow"===e.key?o.key=t?s.C0.ESC+"OD":s.C0.ESC+"[D":"UIKeyInputRightArrow"===e.key?o.key=t?s.C0.ESC+"OC":s.C0.ESC+"[C":"UIKeyInputDownArrow"===e.key&&(o.key=t?s.C0.ESC+"OB":s.C0.ESC+"[B");break;case 8:if(e.altKey){o.key=s.C0.ESC+s.C0.DEL;break}o.key=s.C0.DEL;break;case 9:if(e.shiftKey){o.key=s.C0.ESC+"[Z";break}o.key=s.C0.HT,o.cancel=!0;break;case 13:o.key=e.altKey?s.C0.ESC+s.C0.CR:s.C0.CR,o.cancel=!0;break;case 27:o.key=s.C0.ESC,e.altKey&&(o.key=s.C0.ESC+s.C0.ESC),o.cancel=!0;break;case 37:if(e.metaKey)break;a?(o.key=s.C0.ESC+"[1;"+(a+1)+"D",o.key===s.C0.ESC+"[1;3D"&&(o.key=s.C0.ESC+(i?"b":"[1;5D"))):o.key=t?s.C0.ESC+"OD":s.C0.ESC+"[D";break;case 39:if(e.metaKey)break;a?(o.key=s.C0.ESC+"[1;"+(a+1)+"C",o.key===s.C0.ESC+"[1;3C"&&(o.key=s.C0.ESC+(i?"f":"[1;5C"))):o.key=t?s.C0.ESC+"OC":s.C0.ESC+"[C";break;case 38:if(e.metaKey)break;a?(o.key=s.C0.ESC+"[1;"+(a+1)+"A",i||o.key!==s.C0.ESC+"[1;3A"||(o.key=s.C0.ESC+"[1;5A")):o.key=t?s.C0.ESC+"OA":s.C0.ESC+"[A";break;case 40:if(e.metaKey)break;a?(o.key=s.C0.ESC+"[1;"+(a+1)+"B",i||o.key!==s.C0.ESC+"[1;3B"||(o.key=s.C0.ESC+"[1;5B")):o.key=t?s.C0.ESC+"OB":s.C0.ESC+"[B";break;case 45:e.shiftKey||e.ctrlKey||(o.key=s.C0.ESC+"[2~");break;case 46:o.key=a?s.C0.ESC+"[3;"+(a+1)+"~":s.C0.ESC+"[3~";break;case 36:o.key=a?s.C0.ESC+"[1;"+(a+1)+"H":t?s.C0.ESC+"OH":s.C0.ESC+"[H";break;case 35:o.key=a?s.C0.ESC+"[1;"+(a+1)+"F":t?s.C0.ESC+"OF":s.C0.ESC+"[F";break;case 33:e.shiftKey?o.type=2:e.ctrlKey?o.key=s.C0.ESC+"[5;"+(a+1)+"~":o.key=s.C0.ESC+"[5~";break;case 34:e.shiftKey?o.type=3:e.ctrlKey?o.key=s.C0.ESC+"[6;"+(a+1)+"~":o.key=s.C0.ESC+"[6~";break;case 112:o.key=a?s.C0.ESC+"[1;"+(a+1)+"P":s.C0.ESC+"OP";break;case 113:o.key=a?s.C0.ESC+"[1;"+(a+1)+"Q":s.C0.ESC+"OQ";break;case 114:o.key=a?s.C0.ESC+"[1;"+(a+1)+"R":s.C0.ESC+"OR";break;case 115:o.key=a?s.C0.ESC+"[1;"+(a+1)+"S":s.C0.ESC+"OS";break;case 116:o.key=a?s.C0.ESC+"[15;"+(a+1)+"~":s.C0.ESC+"[15~";break;case 117:o.key=a?s.C0.ESC+"[17;"+(a+1)+"~":s.C0.ESC+"[17~";break;case 118:o.key=a?s.C0.ESC+"[18;"+(a+1)+"~":s.C0.ESC+"[18~";break;case 119:o.key=a?s.C0.ESC+"[19;"+(a+1)+"~":s.C0.ESC+"[19~";break;case 120:o.key=a?s.C0.ESC+"[20;"+(a+1)+"~":s.C0.ESC+"[20~";break;case 121:o.key=a?s.C0.ESC+"[21;"+(a+1)+"~":s.C0.ESC+"[21~";break;case 122:o.key=a?s.C0.ESC+"[23;"+(a+1)+"~":s.C0.ESC+"[23~";break;case 123:o.key=a?s.C0.ESC+"[24;"+(a+1)+"~":s.C0.ESC+"[24~";break;default:if(!e.ctrlKey||e.shiftKey||e.altKey||e.metaKey)if(i&&!n||!e.altKey||e.metaKey)!i||e.altKey||e.ctrlKey||e.shiftKey||!e.metaKey?e.key&&!e.ctrlKey&&!e.altKey&&!e.metaKey&&e.keyCode>=48&&1===e.key.length?o.key=e.key:e.key&&e.ctrlKey&&("_"===e.key&&(o.key=s.C0.US),"@"===e.key&&(o.key=s.C0.NUL)):65===e.keyCode&&(o.type=1);else{const t=r[e.keyCode],i=null==t?void 0:t[e.shiftKey?1:0];if(i)o.key=s.C0.ESC+i;else if(e.keyCode>=65&&e.keyCode<=90){const t=e.ctrlKey?e.keyCode-64:e.keyCode+32;let i=String.fromCharCode(t);e.shiftKey&&(i=i.toUpperCase()),o.key=s.C0.ESC+i}else if(32===e.keyCode)o.key=s.C0.ESC+(e.ctrlKey?s.C0.NUL:" ");else if("Dead"===e.key&&e.code.startsWith("Key")){let t=e.code.slice(3,4);e.shiftKey||(t=t.toLowerCase()),o.key=s.C0.ESC+t,o.cancel=!0}}else e.keyCode>=65&&e.keyCode<=90?o.key=String.fromCharCode(e.keyCode-64):32===e.keyCode?o.key=s.C0.NUL:e.keyCode>=51&&e.keyCode<=55?o.key=String.fromCharCode(e.keyCode-51+27):56===e.keyCode?o.key=s.C0.DEL:219===e.keyCode?o.key=s.C0.ESC:220===e.keyCode?o.key=s.C0.FS:221===e.keyCode&&(o.key=s.C0.GS)}return o}},482:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.Utf8ToUtf32=t.StringToUtf32=t.utf32ToString=t.stringFromCodePoint=void 0,t.stringFromCodePoint=function(e){return e>65535?(e-=65536,String.fromCharCode(55296+(e>>10))+String.fromCharCode(e%1024+56320)):String.fromCharCode(e)},t.utf32ToString=function(e,t=0,i=e.length){let s="";for(let r=t;r65535?(t-=65536,s+=String.fromCharCode(55296+(t>>10))+String.fromCharCode(t%1024+56320)):s+=String.fromCharCode(t)}return s},t.StringToUtf32=class{constructor(){this._interim=0}clear(){this._interim=0}decode(e,t){const i=e.length;if(!i)return 0;let s=0,r=0;if(this._interim){const i=e.charCodeAt(r++);56320<=i&&i<=57343?t[s++]=1024*(this._interim-55296)+i-56320+65536:(t[s++]=this._interim,t[s++]=i),this._interim=0}for(let n=r;n=i)return this._interim=r,s;const o=e.charCodeAt(n);56320<=o&&o<=57343?t[s++]=1024*(r-55296)+o-56320+65536:(t[s++]=r,t[s++]=o)}else 65279!==r&&(t[s++]=r)}return s}},t.Utf8ToUtf32=class{constructor(){this.interim=new Uint8Array(3)}clear(){this.interim.fill(0)}decode(e,t){const i=e.length;if(!i)return 0;let s,r,n,o,a=0,h=0,c=0;if(this.interim[0]){let s=!1,r=this.interim[0];r&=192==(224&r)?31:224==(240&r)?15:7;let n,o=0;for(;(n=63&this.interim[++o])&&o<4;)r<<=6,r|=n;const h=192==(224&this.interim[0])?2:224==(240&this.interim[0])?3:4,l=h-o;for(;c=i)return 0;if(n=e[c++],128!=(192&n)){c--,s=!0;break}this.interim[o++]=n,r<<=6,r|=63&n}s||(2===h?r<128?c--:t[a++]=r:3===h?r<2048||r>=55296&&r<=57343||65279===r||(t[a++]=r):r<65536||r>1114111||(t[a++]=r)),this.interim.fill(0)}const l=i-4;let d=c;for(;d=i)return this.interim[0]=s,a;if(r=e[d++],128!=(192&r)){d--;continue}if(h=(31&s)<<6|63&r,h<128){d--;continue}t[a++]=h}else if(224==(240&s)){if(d>=i)return this.interim[0]=s,a;if(r=e[d++],128!=(192&r)){d--;continue}if(d>=i)return this.interim[0]=s,this.interim[1]=r,a;if(n=e[d++],128!=(192&n)){d--;continue}if(h=(15&s)<<12|(63&r)<<6|63&n,h<2048||h>=55296&&h<=57343||65279===h)continue;t[a++]=h}else if(240==(248&s)){if(d>=i)return this.interim[0]=s,a;if(r=e[d++],128!=(192&r)){d--;continue}if(d>=i)return this.interim[0]=s,this.interim[1]=r,a;if(n=e[d++],128!=(192&n)){d--;continue}if(d>=i)return this.interim[0]=s,this.interim[1]=r,this.interim[2]=n,a;if(o=e[d++],128!=(192&o)){d--;continue}if(h=(7&s)<<18|(63&r)<<12|(63&n)<<6|63&o,h<65536||h>1114111)continue;t[a++]=h}}return a}}},225:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.UnicodeV6=void 0;const i=[[768,879],[1155,1158],[1160,1161],[1425,1469],[1471,1471],[1473,1474],[1476,1477],[1479,1479],[1536,1539],[1552,1557],[1611,1630],[1648,1648],[1750,1764],[1767,1768],[1770,1773],[1807,1807],[1809,1809],[1840,1866],[1958,1968],[2027,2035],[2305,2306],[2364,2364],[2369,2376],[2381,2381],[2385,2388],[2402,2403],[2433,2433],[2492,2492],[2497,2500],[2509,2509],[2530,2531],[2561,2562],[2620,2620],[2625,2626],[2631,2632],[2635,2637],[2672,2673],[2689,2690],[2748,2748],[2753,2757],[2759,2760],[2765,2765],[2786,2787],[2817,2817],[2876,2876],[2879,2879],[2881,2883],[2893,2893],[2902,2902],[2946,2946],[3008,3008],[3021,3021],[3134,3136],[3142,3144],[3146,3149],[3157,3158],[3260,3260],[3263,3263],[3270,3270],[3276,3277],[3298,3299],[3393,3395],[3405,3405],[3530,3530],[3538,3540],[3542,3542],[3633,3633],[3636,3642],[3655,3662],[3761,3761],[3764,3769],[3771,3772],[3784,3789],[3864,3865],[3893,3893],[3895,3895],[3897,3897],[3953,3966],[3968,3972],[3974,3975],[3984,3991],[3993,4028],[4038,4038],[4141,4144],[4146,4146],[4150,4151],[4153,4153],[4184,4185],[4448,4607],[4959,4959],[5906,5908],[5938,5940],[5970,5971],[6002,6003],[6068,6069],[6071,6077],[6086,6086],[6089,6099],[6109,6109],[6155,6157],[6313,6313],[6432,6434],[6439,6440],[6450,6450],[6457,6459],[6679,6680],[6912,6915],[6964,6964],[6966,6970],[6972,6972],[6978,6978],[7019,7027],[7616,7626],[7678,7679],[8203,8207],[8234,8238],[8288,8291],[8298,8303],[8400,8431],[12330,12335],[12441,12442],[43014,43014],[43019,43019],[43045,43046],[64286,64286],[65024,65039],[65056,65059],[65279,65279],[65529,65531]],s=[[68097,68099],[68101,68102],[68108,68111],[68152,68154],[68159,68159],[119143,119145],[119155,119170],[119173,119179],[119210,119213],[119362,119364],[917505,917505],[917536,917631],[917760,917999]];let r;t.UnicodeV6=class{constructor(){if(this.version="6",!r){r=new Uint8Array(65536),r.fill(1),r[0]=0,r.fill(0,1,32),r.fill(0,127,160),r.fill(2,4352,4448),r[9001]=2,r[9002]=2,r.fill(2,11904,42192),r[12351]=1,r.fill(2,44032,55204),r.fill(2,63744,64256),r.fill(2,65040,65050),r.fill(2,65072,65136),r.fill(2,65280,65377),r.fill(2,65504,65511);for(let e=0;et[r][1])return!1;for(;r>=s;)if(i=s+r>>1,e>t[i][1])s=i+1;else{if(!(e=131072&&e<=196605||e>=196608&&e<=262141?2:1}}},5981:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.WriteBuffer=void 0;const s=i(8460),r=i(844);class n extends r.Disposable{constructor(e){super(),this._action=e,this._writeBuffer=[],this._callbacks=[],this._pendingData=0,this._bufferOffset=0,this._isSyncWriting=!1,this._syncCalls=0,this._didUserInput=!1,this._onWriteParsed=this.register(new s.EventEmitter),this.onWriteParsed=this._onWriteParsed.event}handleUserInput(){this._didUserInput=!0}writeSync(e,t){if(void 0!==t&&this._syncCalls>t)return void(this._syncCalls=0);if(this._pendingData+=e.length,this._writeBuffer.push(e),this._callbacks.push(void 0),this._syncCalls++,this._isSyncWriting)return;let i;for(this._isSyncWriting=!0;i=this._writeBuffer.shift();){this._action(i);const e=this._callbacks.shift();e&&e()}this._pendingData=0,this._bufferOffset=2147483647,this._isSyncWriting=!1,this._syncCalls=0}write(e,t){if(this._pendingData>5e7)throw new Error("write data discarded, use flow control to avoid losing data");if(!this._writeBuffer.length){if(this._bufferOffset=0,this._didUserInput)return this._didUserInput=!1,this._pendingData+=e.length,this._writeBuffer.push(e),this._callbacks.push(t),void this._innerWrite();setTimeout((()=>this._innerWrite()))}this._pendingData+=e.length,this._writeBuffer.push(e),this._callbacks.push(t)}_innerWrite(e=0,t=!0){const i=e||Date.now();for(;this._writeBuffer.length>this._bufferOffset;){const e=this._writeBuffer[this._bufferOffset],s=this._action(e,t);if(s){const e=e=>Date.now()-i>=12?setTimeout((()=>this._innerWrite(0,e))):this._innerWrite(i,e);return void s.catch((e=>(queueMicrotask((()=>{throw e})),Promise.resolve(!1)))).then(e)}const r=this._callbacks[this._bufferOffset];if(r&&r(),this._bufferOffset++,this._pendingData-=e.length,Date.now()-i>=12)break}this._writeBuffer.length>this._bufferOffset?(this._bufferOffset>50&&(this._writeBuffer=this._writeBuffer.slice(this._bufferOffset),this._callbacks=this._callbacks.slice(this._bufferOffset),this._bufferOffset=0),setTimeout((()=>this._innerWrite()))):(this._writeBuffer.length=0,this._callbacks.length=0,this._pendingData=0,this._bufferOffset=0),this._onWriteParsed.fire()}}t.WriteBuffer=n},5941:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.toRgbString=t.parseColor=void 0;const i=/^([\da-f])\/([\da-f])\/([\da-f])$|^([\da-f]{2})\/([\da-f]{2})\/([\da-f]{2})$|^([\da-f]{3})\/([\da-f]{3})\/([\da-f]{3})$|^([\da-f]{4})\/([\da-f]{4})\/([\da-f]{4})$/,s=/^[\da-f]+$/;function r(e,t){const i=e.toString(16),s=i.length<2?"0"+i:i;switch(t){case 4:return i[0];case 8:return s;case 12:return(s+s).slice(0,3);default:return s+s}}t.parseColor=function(e){if(!e)return;let t=e.toLowerCase();if(0===t.indexOf("rgb:")){t=t.slice(4);const e=i.exec(t);if(e){const t=e[1]?15:e[4]?255:e[7]?4095:65535;return[Math.round(parseInt(e[1]||e[4]||e[7]||e[10],16)/t*255),Math.round(parseInt(e[2]||e[5]||e[8]||e[11],16)/t*255),Math.round(parseInt(e[3]||e[6]||e[9]||e[12],16)/t*255)]}}else if(0===t.indexOf("#")&&(t=t.slice(1),s.exec(t)&&[3,6,9,12].includes(t.length))){const e=t.length/3,i=[0,0,0];for(let s=0;s<3;++s){const r=parseInt(t.slice(e*s,e*s+e),16);i[s]=1===e?r<<4:2===e?r:3===e?r>>4:r>>8}return i}},t.toRgbString=function(e,t=16){const[i,s,n]=e;return`rgb:${r(i,t)}/${r(s,t)}/${r(n,t)}`}},5770:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.PAYLOAD_LIMIT=void 0,t.PAYLOAD_LIMIT=1e7},6351:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.DcsHandler=t.DcsParser=void 0;const s=i(482),r=i(8742),n=i(5770),o=[];t.DcsParser=class{constructor(){this._handlers=Object.create(null),this._active=o,this._ident=0,this._handlerFb=()=>{},this._stack={paused:!1,loopPosition:0,fallThrough:!1}}dispose(){this._handlers=Object.create(null),this._handlerFb=()=>{},this._active=o}registerHandler(e,t){void 0===this._handlers[e]&&(this._handlers[e]=[]);const i=this._handlers[e];return i.push(t),{dispose:()=>{const e=i.indexOf(t);-1!==e&&i.splice(e,1)}}}clearHandler(e){this._handlers[e]&&delete this._handlers[e]}setHandlerFallback(e){this._handlerFb=e}reset(){if(this._active.length)for(let e=this._stack.paused?this._stack.loopPosition-1:this._active.length-1;e>=0;--e)this._active[e].unhook(!1);this._stack.paused=!1,this._active=o,this._ident=0}hook(e,t){if(this.reset(),this._ident=e,this._active=this._handlers[e]||o,this._active.length)for(let e=this._active.length-1;e>=0;e--)this._active[e].hook(t);else this._handlerFb(this._ident,"HOOK",t)}put(e,t,i){if(this._active.length)for(let s=this._active.length-1;s>=0;s--)this._active[s].put(e,t,i);else this._handlerFb(this._ident,"PUT",(0,s.utf32ToString)(e,t,i))}unhook(e,t=!0){if(this._active.length){let i=!1,s=this._active.length-1,r=!1;if(this._stack.paused&&(s=this._stack.loopPosition-1,i=t,r=this._stack.fallThrough,this._stack.paused=!1),!r&&!1===i){for(;s>=0&&(i=this._active[s].unhook(e),!0!==i);s--)if(i instanceof Promise)return this._stack.paused=!0,this._stack.loopPosition=s,this._stack.fallThrough=!1,i;s--}for(;s>=0;s--)if(i=this._active[s].unhook(!1),i instanceof Promise)return this._stack.paused=!0,this._stack.loopPosition=s,this._stack.fallThrough=!0,i}else this._handlerFb(this._ident,"UNHOOK",e);this._active=o,this._ident=0}};const a=new r.Params;a.addParam(0),t.DcsHandler=class{constructor(e){this._handler=e,this._data="",this._params=a,this._hitLimit=!1}hook(e){this._params=e.length>1||e.params[0]?e.clone():a,this._data="",this._hitLimit=!1}put(e,t,i){this._hitLimit||(this._data+=(0,s.utf32ToString)(e,t,i),this._data.length>n.PAYLOAD_LIMIT&&(this._data="",this._hitLimit=!0))}unhook(e){let t=!1;if(this._hitLimit)t=!1;else if(e&&(t=this._handler(this._data,this._params),t instanceof Promise))return t.then((e=>(this._params=a,this._data="",this._hitLimit=!1,e)));return this._params=a,this._data="",this._hitLimit=!1,t}}},2015:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.EscapeSequenceParser=t.VT500_TRANSITION_TABLE=t.TransitionTable=void 0;const s=i(844),r=i(8742),n=i(6242),o=i(6351);class a{constructor(e){this.table=new Uint8Array(e)}setDefault(e,t){this.table.fill(e<<4|t)}add(e,t,i,s){this.table[t<<8|e]=i<<4|s}addMany(e,t,i,s){for(let r=0;rt)),i=(e,i)=>t.slice(e,i),s=i(32,127),r=i(0,24);r.push(25),r.push.apply(r,i(28,32));const n=i(0,14);let o;for(o in e.setDefault(1,0),e.addMany(s,0,2,0),n)e.addMany([24,26,153,154],o,3,0),e.addMany(i(128,144),o,3,0),e.addMany(i(144,152),o,3,0),e.add(156,o,0,0),e.add(27,o,11,1),e.add(157,o,4,8),e.addMany([152,158,159],o,0,7),e.add(155,o,11,3),e.add(144,o,11,9);return e.addMany(r,0,3,0),e.addMany(r,1,3,1),e.add(127,1,0,1),e.addMany(r,8,0,8),e.addMany(r,3,3,3),e.add(127,3,0,3),e.addMany(r,4,3,4),e.add(127,4,0,4),e.addMany(r,6,3,6),e.addMany(r,5,3,5),e.add(127,5,0,5),e.addMany(r,2,3,2),e.add(127,2,0,2),e.add(93,1,4,8),e.addMany(s,8,5,8),e.add(127,8,5,8),e.addMany([156,27,24,26,7],8,6,0),e.addMany(i(28,32),8,0,8),e.addMany([88,94,95],1,0,7),e.addMany(s,7,0,7),e.addMany(r,7,0,7),e.add(156,7,0,0),e.add(127,7,0,7),e.add(91,1,11,3),e.addMany(i(64,127),3,7,0),e.addMany(i(48,60),3,8,4),e.addMany([60,61,62,63],3,9,4),e.addMany(i(48,60),4,8,4),e.addMany(i(64,127),4,7,0),e.addMany([60,61,62,63],4,0,6),e.addMany(i(32,64),6,0,6),e.add(127,6,0,6),e.addMany(i(64,127),6,0,0),e.addMany(i(32,48),3,9,5),e.addMany(i(32,48),5,9,5),e.addMany(i(48,64),5,0,6),e.addMany(i(64,127),5,7,0),e.addMany(i(32,48),4,9,5),e.addMany(i(32,48),1,9,2),e.addMany(i(32,48),2,9,2),e.addMany(i(48,127),2,10,0),e.addMany(i(48,80),1,10,0),e.addMany(i(81,88),1,10,0),e.addMany([89,90,92],1,10,0),e.addMany(i(96,127),1,10,0),e.add(80,1,11,9),e.addMany(r,9,0,9),e.add(127,9,0,9),e.addMany(i(28,32),9,0,9),e.addMany(i(32,48),9,9,12),e.addMany(i(48,60),9,8,10),e.addMany([60,61,62,63],9,9,10),e.addMany(r,11,0,11),e.addMany(i(32,128),11,0,11),e.addMany(i(28,32),11,0,11),e.addMany(r,10,0,10),e.add(127,10,0,10),e.addMany(i(28,32),10,0,10),e.addMany(i(48,60),10,8,10),e.addMany([60,61,62,63],10,0,11),e.addMany(i(32,48),10,9,12),e.addMany(r,12,0,12),e.add(127,12,0,12),e.addMany(i(28,32),12,0,12),e.addMany(i(32,48),12,9,12),e.addMany(i(48,64),12,0,11),e.addMany(i(64,127),12,12,13),e.addMany(i(64,127),10,12,13),e.addMany(i(64,127),9,12,13),e.addMany(r,13,13,13),e.addMany(s,13,13,13),e.add(127,13,0,13),e.addMany([27,156,24,26],13,14,0),e.add(h,0,2,0),e.add(h,8,5,8),e.add(h,6,0,6),e.add(h,11,0,11),e.add(h,13,13,13),e}();class c extends s.Disposable{constructor(e=t.VT500_TRANSITION_TABLE){super(),this._transitions=e,this._parseStack={state:0,handlers:[],handlerPos:0,transition:0,chunkPos:0},this.initialState=0,this.currentState=this.initialState,this._params=new r.Params,this._params.addParam(0),this._collect=0,this.precedingCodepoint=0,this._printHandlerFb=(e,t,i)=>{},this._executeHandlerFb=e=>{},this._csiHandlerFb=(e,t)=>{},this._escHandlerFb=e=>{},this._errorHandlerFb=e=>e,this._printHandler=this._printHandlerFb,this._executeHandlers=Object.create(null),this._csiHandlers=Object.create(null),this._escHandlers=Object.create(null),this.register((0,s.toDisposable)((()=>{this._csiHandlers=Object.create(null),this._executeHandlers=Object.create(null),this._escHandlers=Object.create(null)}))),this._oscParser=this.register(new n.OscParser),this._dcsParser=this.register(new o.DcsParser),this._errorHandler=this._errorHandlerFb,this.registerEscHandler({final:"\\"},(()=>!0))}_identifier(e,t=[64,126]){let i=0;if(e.prefix){if(e.prefix.length>1)throw new Error("only one byte as prefix supported");if(i=e.prefix.charCodeAt(0),i&&60>i||i>63)throw new Error("prefix must be in range 0x3c .. 0x3f")}if(e.intermediates){if(e.intermediates.length>2)throw new Error("only two bytes as intermediates are supported");for(let t=0;ts||s>47)throw new Error("intermediate must be in range 0x20 .. 0x2f");i<<=8,i|=s}}if(1!==e.final.length)throw new Error("final must be a single byte");const s=e.final.charCodeAt(0);if(t[0]>s||s>t[1])throw new Error(`final must be in range ${t[0]} .. ${t[1]}`);return i<<=8,i|=s,i}identToString(e){const t=[];for(;e;)t.push(String.fromCharCode(255&e)),e>>=8;return t.reverse().join("")}setPrintHandler(e){this._printHandler=e}clearPrintHandler(){this._printHandler=this._printHandlerFb}registerEscHandler(e,t){const i=this._identifier(e,[48,126]);void 0===this._escHandlers[i]&&(this._escHandlers[i]=[]);const s=this._escHandlers[i];return s.push(t),{dispose:()=>{const e=s.indexOf(t);-1!==e&&s.splice(e,1)}}}clearEscHandler(e){this._escHandlers[this._identifier(e,[48,126])]&&delete this._escHandlers[this._identifier(e,[48,126])]}setEscHandlerFallback(e){this._escHandlerFb=e}setExecuteHandler(e,t){this._executeHandlers[e.charCodeAt(0)]=t}clearExecuteHandler(e){this._executeHandlers[e.charCodeAt(0)]&&delete this._executeHandlers[e.charCodeAt(0)]}setExecuteHandlerFallback(e){this._executeHandlerFb=e}registerCsiHandler(e,t){const i=this._identifier(e);void 0===this._csiHandlers[i]&&(this._csiHandlers[i]=[]);const s=this._csiHandlers[i];return s.push(t),{dispose:()=>{const e=s.indexOf(t);-1!==e&&s.splice(e,1)}}}clearCsiHandler(e){this._csiHandlers[this._identifier(e)]&&delete this._csiHandlers[this._identifier(e)]}setCsiHandlerFallback(e){this._csiHandlerFb=e}registerDcsHandler(e,t){return this._dcsParser.registerHandler(this._identifier(e),t)}clearDcsHandler(e){this._dcsParser.clearHandler(this._identifier(e))}setDcsHandlerFallback(e){this._dcsParser.setHandlerFallback(e)}registerOscHandler(e,t){return this._oscParser.registerHandler(e,t)}clearOscHandler(e){this._oscParser.clearHandler(e)}setOscHandlerFallback(e){this._oscParser.setHandlerFallback(e)}setErrorHandler(e){this._errorHandler=e}clearErrorHandler(){this._errorHandler=this._errorHandlerFb}reset(){this.currentState=this.initialState,this._oscParser.reset(),this._dcsParser.reset(),this._params.reset(),this._params.addParam(0),this._collect=0,this.precedingCodepoint=0,0!==this._parseStack.state&&(this._parseStack.state=2,this._parseStack.handlers=[])}_preserveStack(e,t,i,s,r){this._parseStack.state=e,this._parseStack.handlers=t,this._parseStack.handlerPos=i,this._parseStack.transition=s,this._parseStack.chunkPos=r}parse(e,t,i){let s,r=0,n=0,o=0;if(this._parseStack.state)if(2===this._parseStack.state)this._parseStack.state=0,o=this._parseStack.chunkPos+1;else{if(void 0===i||1===this._parseStack.state)throw this._parseStack.state=1,new Error("improper continuation due to previous async handler, giving up parsing");const t=this._parseStack.handlers;let n=this._parseStack.handlerPos-1;switch(this._parseStack.state){case 3:if(!1===i&&n>-1)for(;n>=0&&(s=t[n](this._params),!0!==s);n--)if(s instanceof Promise)return this._parseStack.handlerPos=n,s;this._parseStack.handlers=[];break;case 4:if(!1===i&&n>-1)for(;n>=0&&(s=t[n](),!0!==s);n--)if(s instanceof Promise)return this._parseStack.handlerPos=n,s;this._parseStack.handlers=[];break;case 6:if(r=e[this._parseStack.chunkPos],s=this._dcsParser.unhook(24!==r&&26!==r,i),s)return s;27===r&&(this._parseStack.transition|=1),this._params.reset(),this._params.addParam(0),this._collect=0;break;case 5:if(r=e[this._parseStack.chunkPos],s=this._oscParser.end(24!==r&&26!==r,i),s)return s;27===r&&(this._parseStack.transition|=1),this._params.reset(),this._params.addParam(0),this._collect=0}this._parseStack.state=0,o=this._parseStack.chunkPos+1,this.precedingCodepoint=0,this.currentState=15&this._parseStack.transition}for(let i=o;i>4){case 2:for(let s=i+1;;++s){if(s>=t||(r=e[s])<32||r>126&&r=t||(r=e[s])<32||r>126&&r=t||(r=e[s])<32||r>126&&r=t||(r=e[s])<32||r>126&&r=0&&(s=o[a](this._params),!0!==s);a--)if(s instanceof Promise)return this._preserveStack(3,o,a,n,i),s;a<0&&this._csiHandlerFb(this._collect<<8|r,this._params),this.precedingCodepoint=0;break;case 8:do{switch(r){case 59:this._params.addParam(0);break;case 58:this._params.addSubParam(-1);break;default:this._params.addDigit(r-48)}}while(++i47&&r<60);i--;break;case 9:this._collect<<=8,this._collect|=r;break;case 10:const c=this._escHandlers[this._collect<<8|r];let l=c?c.length-1:-1;for(;l>=0&&(s=c[l](),!0!==s);l--)if(s instanceof Promise)return this._preserveStack(4,c,l,n,i),s;l<0&&this._escHandlerFb(this._collect<<8|r),this.precedingCodepoint=0;break;case 11:this._params.reset(),this._params.addParam(0),this._collect=0;break;case 12:this._dcsParser.hook(this._collect<<8|r,this._params);break;case 13:for(let s=i+1;;++s)if(s>=t||24===(r=e[s])||26===r||27===r||r>127&&r=t||(r=e[s])<32||r>127&&r{Object.defineProperty(t,"__esModule",{value:!0}),t.OscHandler=t.OscParser=void 0;const s=i(5770),r=i(482),n=[];t.OscParser=class{constructor(){this._state=0,this._active=n,this._id=-1,this._handlers=Object.create(null),this._handlerFb=()=>{},this._stack={paused:!1,loopPosition:0,fallThrough:!1}}registerHandler(e,t){void 0===this._handlers[e]&&(this._handlers[e]=[]);const i=this._handlers[e];return i.push(t),{dispose:()=>{const e=i.indexOf(t);-1!==e&&i.splice(e,1)}}}clearHandler(e){this._handlers[e]&&delete this._handlers[e]}setHandlerFallback(e){this._handlerFb=e}dispose(){this._handlers=Object.create(null),this._handlerFb=()=>{},this._active=n}reset(){if(2===this._state)for(let e=this._stack.paused?this._stack.loopPosition-1:this._active.length-1;e>=0;--e)this._active[e].end(!1);this._stack.paused=!1,this._active=n,this._id=-1,this._state=0}_start(){if(this._active=this._handlers[this._id]||n,this._active.length)for(let e=this._active.length-1;e>=0;e--)this._active[e].start();else this._handlerFb(this._id,"START")}_put(e,t,i){if(this._active.length)for(let s=this._active.length-1;s>=0;s--)this._active[s].put(e,t,i);else this._handlerFb(this._id,"PUT",(0,r.utf32ToString)(e,t,i))}start(){this.reset(),this._state=1}put(e,t,i){if(3!==this._state){if(1===this._state)for(;t0&&this._put(e,t,i)}}end(e,t=!0){if(0!==this._state){if(3!==this._state)if(1===this._state&&this._start(),this._active.length){let i=!1,s=this._active.length-1,r=!1;if(this._stack.paused&&(s=this._stack.loopPosition-1,i=t,r=this._stack.fallThrough,this._stack.paused=!1),!r&&!1===i){for(;s>=0&&(i=this._active[s].end(e),!0!==i);s--)if(i instanceof Promise)return this._stack.paused=!0,this._stack.loopPosition=s,this._stack.fallThrough=!1,i;s--}for(;s>=0;s--)if(i=this._active[s].end(!1),i instanceof Promise)return this._stack.paused=!0,this._stack.loopPosition=s,this._stack.fallThrough=!0,i}else this._handlerFb(this._id,"END",e);this._active=n,this._id=-1,this._state=0}}},t.OscHandler=class{constructor(e){this._handler=e,this._data="",this._hitLimit=!1}start(){this._data="",this._hitLimit=!1}put(e,t,i){this._hitLimit||(this._data+=(0,r.utf32ToString)(e,t,i),this._data.length>s.PAYLOAD_LIMIT&&(this._data="",this._hitLimit=!0))}end(e){let t=!1;if(this._hitLimit)t=!1;else if(e&&(t=this._handler(this._data),t instanceof Promise))return t.then((e=>(this._data="",this._hitLimit=!1,e)));return this._data="",this._hitLimit=!1,t}}},8742:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.Params=void 0;const i=2147483647;class s{static fromArray(e){const t=new s;if(!e.length)return t;for(let i=Array.isArray(e[0])?1:0;i256)throw new Error("maxSubParamsLength must not be greater than 256");this.params=new Int32Array(e),this.length=0,this._subParams=new Int32Array(t),this._subParamsLength=0,this._subParamsIdx=new Uint16Array(e),this._rejectDigits=!1,this._rejectSubDigits=!1,this._digitIsSub=!1}clone(){const e=new s(this.maxLength,this.maxSubParamsLength);return e.params.set(this.params),e.length=this.length,e._subParams.set(this._subParams),e._subParamsLength=this._subParamsLength,e._subParamsIdx.set(this._subParamsIdx),e._rejectDigits=this._rejectDigits,e._rejectSubDigits=this._rejectSubDigits,e._digitIsSub=this._digitIsSub,e}toArray(){const e=[];for(let t=0;t>8,s=255&this._subParamsIdx[t];s-i>0&&e.push(Array.prototype.slice.call(this._subParams,i,s))}return e}reset(){this.length=0,this._subParamsLength=0,this._rejectDigits=!1,this._rejectSubDigits=!1,this._digitIsSub=!1}addParam(e){if(this._digitIsSub=!1,this.length>=this.maxLength)this._rejectDigits=!0;else{if(e<-1)throw new Error("values lesser than -1 are not allowed");this._subParamsIdx[this.length]=this._subParamsLength<<8|this._subParamsLength,this.params[this.length++]=e>i?i:e}}addSubParam(e){if(this._digitIsSub=!0,this.length)if(this._rejectDigits||this._subParamsLength>=this.maxSubParamsLength)this._rejectSubDigits=!0;else{if(e<-1)throw new Error("values lesser than -1 are not allowed");this._subParams[this._subParamsLength++]=e>i?i:e,this._subParamsIdx[this.length-1]++}}hasSubParams(e){return(255&this._subParamsIdx[e])-(this._subParamsIdx[e]>>8)>0}getSubParams(e){const t=this._subParamsIdx[e]>>8,i=255&this._subParamsIdx[e];return i-t>0?this._subParams.subarray(t,i):null}getSubParamsAll(){const e={};for(let t=0;t>8,s=255&this._subParamsIdx[t];s-i>0&&(e[t]=this._subParams.slice(i,s))}return e}addDigit(e){let t;if(this._rejectDigits||!(t=this._digitIsSub?this._subParamsLength:this.length)||this._digitIsSub&&this._rejectSubDigits)return;const s=this._digitIsSub?this._subParams:this.params,r=s[t-1];s[t-1]=~r?Math.min(10*r+e,i):e}}t.Params=s},5741:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.AddonManager=void 0,t.AddonManager=class{constructor(){this._addons=[]}dispose(){for(let e=this._addons.length-1;e>=0;e--)this._addons[e].instance.dispose()}loadAddon(e,t){const i={instance:t,dispose:t.dispose,isDisposed:!1};this._addons.push(i),t.dispose=()=>this._wrappedAddonDispose(i),t.activate(e)}_wrappedAddonDispose(e){if(e.isDisposed)return;let t=-1;for(let i=0;i{Object.defineProperty(t,"__esModule",{value:!0}),t.BufferApiView=void 0;const s=i(3785),r=i(511);t.BufferApiView=class{constructor(e,t){this._buffer=e,this.type=t}init(e){return this._buffer=e,this}get cursorY(){return this._buffer.y}get cursorX(){return this._buffer.x}get viewportY(){return this._buffer.ydisp}get baseY(){return this._buffer.ybase}get length(){return this._buffer.lines.length}getLine(e){const t=this._buffer.lines.get(e);if(t)return new s.BufferLineApiView(t)}getNullCell(){return new r.CellData}}},3785:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.BufferLineApiView=void 0;const s=i(511);t.BufferLineApiView=class{constructor(e){this._line=e}get isWrapped(){return this._line.isWrapped}get length(){return this._line.length}getCell(e,t){if(!(e<0||e>=this._line.length))return t?(this._line.loadCell(e,t),t):this._line.loadCell(e,new s.CellData)}translateToString(e,t,i){return this._line.translateToString(e,t,i)}}},8285:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.BufferNamespaceApi=void 0;const s=i(8771),r=i(8460),n=i(844);class o extends n.Disposable{constructor(e){super(),this._core=e,this._onBufferChange=this.register(new r.EventEmitter),this.onBufferChange=this._onBufferChange.event,this._normal=new s.BufferApiView(this._core.buffers.normal,"normal"),this._alternate=new s.BufferApiView(this._core.buffers.alt,"alternate"),this._core.buffers.onBufferActivate((()=>this._onBufferChange.fire(this.active)))}get active(){if(this._core.buffers.active===this._core.buffers.normal)return this.normal;if(this._core.buffers.active===this._core.buffers.alt)return this.alternate;throw new Error("Active buffer is neither normal nor alternate")}get normal(){return this._normal.init(this._core.buffers.normal)}get alternate(){return this._alternate.init(this._core.buffers.alt)}}t.BufferNamespaceApi=o},7975:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.ParserApi=void 0,t.ParserApi=class{constructor(e){this._core=e}registerCsiHandler(e,t){return this._core.registerCsiHandler(e,(e=>t(e.toArray())))}addCsiHandler(e,t){return this.registerCsiHandler(e,t)}registerDcsHandler(e,t){return this._core.registerDcsHandler(e,((e,i)=>t(e,i.toArray())))}addDcsHandler(e,t){return this.registerDcsHandler(e,t)}registerEscHandler(e,t){return this._core.registerEscHandler(e,t)}addEscHandler(e,t){return this.registerEscHandler(e,t)}registerOscHandler(e,t){return this._core.registerOscHandler(e,t)}addOscHandler(e,t){return this.registerOscHandler(e,t)}}},7090:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.UnicodeApi=void 0,t.UnicodeApi=class{constructor(e){this._core=e}register(e){this._core.unicodeService.register(e)}get versions(){return this._core.unicodeService.versions}get activeVersion(){return this._core.unicodeService.activeVersion}set activeVersion(e){this._core.unicodeService.activeVersion=e}}},744:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.BufferService=t.MINIMUM_ROWS=t.MINIMUM_COLS=void 0;const n=i(8460),o=i(844),a=i(5295),h=i(2585);t.MINIMUM_COLS=2,t.MINIMUM_ROWS=1;let c=t.BufferService=class extends o.Disposable{get buffer(){return this.buffers.active}constructor(e){super(),this.isUserScrolling=!1,this._onResize=this.register(new n.EventEmitter),this.onResize=this._onResize.event,this._onScroll=this.register(new n.EventEmitter),this.onScroll=this._onScroll.event,this.cols=Math.max(e.rawOptions.cols||0,t.MINIMUM_COLS),this.rows=Math.max(e.rawOptions.rows||0,t.MINIMUM_ROWS),this.buffers=this.register(new a.BufferSet(e,this))}resize(e,t){this.cols=e,this.rows=t,this.buffers.resize(e,t),this._onResize.fire({cols:e,rows:t})}reset(){this.buffers.reset(),this.isUserScrolling=!1}scroll(e,t=!1){const i=this.buffer;let s;s=this._cachedBlankLine,s&&s.length===this.cols&&s.getFg(0)===e.fg&&s.getBg(0)===e.bg||(s=i.getBlankLine(e,t),this._cachedBlankLine=s),s.isWrapped=t;const r=i.ybase+i.scrollTop,n=i.ybase+i.scrollBottom;if(0===i.scrollTop){const e=i.lines.isFull;n===i.lines.length-1?e?i.lines.recycle().copyFrom(s):i.lines.push(s.clone()):i.lines.splice(n+1,0,s.clone()),e?this.isUserScrolling&&(i.ydisp=Math.max(i.ydisp-1,0)):(i.ybase++,this.isUserScrolling||i.ydisp++)}else{const e=n-r+1;i.lines.shiftElements(r+1,e-1,-1),i.lines.set(n,s.clone())}this.isUserScrolling||(i.ydisp=i.ybase),this._onScroll.fire(i.ydisp)}scrollLines(e,t,i){const s=this.buffer;if(e<0){if(0===s.ydisp)return;this.isUserScrolling=!0}else e+s.ydisp>=s.ybase&&(this.isUserScrolling=!1);const r=s.ydisp;s.ydisp=Math.max(Math.min(s.ydisp+e,s.ybase),0),r!==s.ydisp&&(t||this._onScroll.fire(s.ydisp))}};t.BufferService=c=s([r(0,h.IOptionsService)],c)},7994:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.CharsetService=void 0,t.CharsetService=class{constructor(){this.glevel=0,this._charsets=[]}reset(){this.charset=void 0,this._charsets=[],this.glevel=0}setgLevel(e){this.glevel=e,this.charset=this._charsets[e]}setgCharset(e,t){this._charsets[e]=t,this.glevel===e&&(this.charset=t)}}},1753:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.CoreMouseService=void 0;const n=i(2585),o=i(8460),a=i(844),h={NONE:{events:0,restrict:()=>!1},X10:{events:1,restrict:e=>4!==e.button&&1===e.action&&(e.ctrl=!1,e.alt=!1,e.shift=!1,!0)},VT200:{events:19,restrict:e=>32!==e.action},DRAG:{events:23,restrict:e=>32!==e.action||3!==e.button},ANY:{events:31,restrict:e=>!0}};function c(e,t){let i=(e.ctrl?16:0)|(e.shift?4:0)|(e.alt?8:0);return 4===e.button?(i|=64,i|=e.action):(i|=3&e.button,4&e.button&&(i|=64),8&e.button&&(i|=128),32===e.action?i|=32:0!==e.action||t||(i|=3)),i}const l=String.fromCharCode,d={DEFAULT:e=>{const t=[c(e,!1)+32,e.col+32,e.row+32];return t[0]>255||t[1]>255||t[2]>255?"":`${l(t[0])}${l(t[1])}${l(t[2])}`},SGR:e=>{const t=0===e.action&&4!==e.button?"m":"M";return`[<${c(e,!0)};${e.col};${e.row}${t}`},SGR_PIXELS:e=>{const t=0===e.action&&4!==e.button?"m":"M";return`[<${c(e,!0)};${e.x};${e.y}${t}`}};let _=t.CoreMouseService=class extends a.Disposable{constructor(e,t){super(),this._bufferService=e,this._coreService=t,this._protocols={},this._encodings={},this._activeProtocol="",this._activeEncoding="",this._lastEvent=null,this._onProtocolChange=this.register(new o.EventEmitter),this.onProtocolChange=this._onProtocolChange.event;for(const e of Object.keys(h))this.addProtocol(e,h[e]);for(const e of Object.keys(d))this.addEncoding(e,d[e]);this.reset()}addProtocol(e,t){this._protocols[e]=t}addEncoding(e,t){this._encodings[e]=t}get activeProtocol(){return this._activeProtocol}get areMouseEventsActive(){return 0!==this._protocols[this._activeProtocol].events}set activeProtocol(e){if(!this._protocols[e])throw new Error(`unknown protocol "${e}"`);this._activeProtocol=e,this._onProtocolChange.fire(this._protocols[e].events)}get activeEncoding(){return this._activeEncoding}set activeEncoding(e){if(!this._encodings[e])throw new Error(`unknown encoding "${e}"`);this._activeEncoding=e}reset(){this.activeProtocol="NONE",this.activeEncoding="DEFAULT",this._lastEvent=null}triggerMouseEvent(e){if(e.col<0||e.col>=this._bufferService.cols||e.row<0||e.row>=this._bufferService.rows)return!1;if(4===e.button&&32===e.action)return!1;if(3===e.button&&32!==e.action)return!1;if(4!==e.button&&(2===e.action||3===e.action))return!1;if(e.col++,e.row++,32===e.action&&this._lastEvent&&this._equalEvents(this._lastEvent,e,"SGR_PIXELS"===this._activeEncoding))return!1;if(!this._protocols[this._activeProtocol].restrict(e))return!1;const t=this._encodings[this._activeEncoding](e);return t&&("DEFAULT"===this._activeEncoding?this._coreService.triggerBinaryEvent(t):this._coreService.triggerDataEvent(t,!0)),this._lastEvent=e,!0}explainEvents(e){return{down:!!(1&e),up:!!(2&e),drag:!!(4&e),move:!!(8&e),wheel:!!(16&e)}}_equalEvents(e,t,i){if(i){if(e.x!==t.x)return!1;if(e.y!==t.y)return!1}else{if(e.col!==t.col)return!1;if(e.row!==t.row)return!1}return e.button===t.button&&e.action===t.action&&e.ctrl===t.ctrl&&e.alt===t.alt&&e.shift===t.shift}};t.CoreMouseService=_=s([r(0,n.IBufferService),r(1,n.ICoreService)],_)},6975:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.CoreService=void 0;const n=i(1439),o=i(8460),a=i(844),h=i(2585),c=Object.freeze({insertMode:!1}),l=Object.freeze({applicationCursorKeys:!1,applicationKeypad:!1,bracketedPasteMode:!1,origin:!1,reverseWraparound:!1,sendFocus:!1,wraparound:!0});let d=t.CoreService=class extends a.Disposable{constructor(e,t,i){super(),this._bufferService=e,this._logService=t,this._optionsService=i,this.isCursorInitialized=!1,this.isCursorHidden=!1,this._onData=this.register(new o.EventEmitter),this.onData=this._onData.event,this._onUserInput=this.register(new o.EventEmitter),this.onUserInput=this._onUserInput.event,this._onBinary=this.register(new o.EventEmitter),this.onBinary=this._onBinary.event,this._onRequestScrollToBottom=this.register(new o.EventEmitter),this.onRequestScrollToBottom=this._onRequestScrollToBottom.event,this.modes=(0,n.clone)(c),this.decPrivateModes=(0,n.clone)(l)}reset(){this.modes=(0,n.clone)(c),this.decPrivateModes=(0,n.clone)(l)}triggerDataEvent(e,t=!1){if(this._optionsService.rawOptions.disableStdin)return;const i=this._bufferService.buffer;t&&this._optionsService.rawOptions.scrollOnUserInput&&i.ybase!==i.ydisp&&this._onRequestScrollToBottom.fire(),t&&this._onUserInput.fire(),this._logService.debug(`sending data "${e}"`,(()=>e.split("").map((e=>e.charCodeAt(0))))),this._onData.fire(e)}triggerBinaryEvent(e){this._optionsService.rawOptions.disableStdin||(this._logService.debug(`sending binary "${e}"`,(()=>e.split("").map((e=>e.charCodeAt(0))))),this._onBinary.fire(e))}};t.CoreService=d=s([r(0,h.IBufferService),r(1,h.ILogService),r(2,h.IOptionsService)],d)},9074:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.DecorationService=void 0;const s=i(8055),r=i(8460),n=i(844),o=i(6106);let a=0,h=0;class c extends n.Disposable{get decorations(){return this._decorations.values()}constructor(){super(),this._decorations=new o.SortedList((e=>null==e?void 0:e.marker.line)),this._onDecorationRegistered=this.register(new r.EventEmitter),this.onDecorationRegistered=this._onDecorationRegistered.event,this._onDecorationRemoved=this.register(new r.EventEmitter),this.onDecorationRemoved=this._onDecorationRemoved.event,this.register((0,n.toDisposable)((()=>this.reset())))}registerDecoration(e){if(e.marker.isDisposed)return;const t=new l(e);if(t){const e=t.marker.onDispose((()=>t.dispose()));t.onDispose((()=>{t&&(this._decorations.delete(t)&&this._onDecorationRemoved.fire(t),e.dispose())})),this._decorations.insert(t),this._onDecorationRegistered.fire(t)}return t}reset(){for(const e of this._decorations.values())e.dispose();this._decorations.clear()}*getDecorationsAtCell(e,t,i){var s,r,n;let o=0,a=0;for(const h of this._decorations.getKeyIterator(t))o=null!==(s=h.options.x)&&void 0!==s?s:0,a=o+(null!==(r=h.options.width)&&void 0!==r?r:1),e>=o&&e{var r,n,o;a=null!==(r=t.options.x)&&void 0!==r?r:0,h=a+(null!==(n=t.options.width)&&void 0!==n?n:1),e>=a&&e{Object.defineProperty(t,"__esModule",{value:!0}),t.InstantiationService=t.ServiceCollection=void 0;const s=i(2585),r=i(8343);class n{constructor(...e){this._entries=new Map;for(const[t,i]of e)this.set(t,i)}set(e,t){const i=this._entries.get(e);return this._entries.set(e,t),i}forEach(e){for(const[t,i]of this._entries.entries())e(t,i)}has(e){return this._entries.has(e)}get(e){return this._entries.get(e)}}t.ServiceCollection=n,t.InstantiationService=class{constructor(){this._services=new n,this._services.set(s.IInstantiationService,this)}setService(e,t){this._services.set(e,t)}getService(e){return this._services.get(e)}createInstance(e,...t){const i=(0,r.getServiceDependencies)(e).sort(((e,t)=>e.index-t.index)),s=[];for(const t of i){const i=this._services.get(t.id);if(!i)throw new Error(`[createInstance] ${e.name} depends on UNKNOWN service ${t.id}.`);s.push(i)}const n=i.length>0?i[0].index:t.length;if(t.length!==n)throw new Error(`[createInstance] First service dependency of ${e.name} at position ${n+1} conflicts with ${t.length} static arguments`);return new e(...[...t,...s])}}},7866:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.traceCall=t.setTraceLogger=t.LogService=void 0;const n=i(844),o=i(2585),a={trace:o.LogLevelEnum.TRACE,debug:o.LogLevelEnum.DEBUG,info:o.LogLevelEnum.INFO,warn:o.LogLevelEnum.WARN,error:o.LogLevelEnum.ERROR,off:o.LogLevelEnum.OFF};let h,c=t.LogService=class extends n.Disposable{get logLevel(){return this._logLevel}constructor(e){super(),this._optionsService=e,this._logLevel=o.LogLevelEnum.OFF,this._updateLogLevel(),this.register(this._optionsService.onSpecificOptionChange("logLevel",(()=>this._updateLogLevel()))),h=this}_updateLogLevel(){this._logLevel=a[this._optionsService.rawOptions.logLevel]}_evalLazyOptionalParams(e){for(let t=0;tJSON.stringify(e))).join(", ")})`);const t=s.apply(this,e);return h.trace(`GlyphRenderer#${s.name} return`,t),t}}},7302:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.OptionsService=t.DEFAULT_OPTIONS=void 0;const s=i(8460),r=i(844),n=i(6114);t.DEFAULT_OPTIONS={cols:80,rows:24,cursorBlink:!1,cursorStyle:"block",cursorWidth:1,cursorInactiveStyle:"outline",customGlyphs:!0,drawBoldTextInBrightColors:!0,fastScrollModifier:"alt",fastScrollSensitivity:5,fontFamily:"courier-new, courier, monospace",fontSize:15,fontWeight:"normal",fontWeightBold:"bold",ignoreBracketedPasteMode:!1,lineHeight:1,letterSpacing:0,linkHandler:null,logLevel:"info",logger:null,scrollback:1e3,scrollOnUserInput:!0,scrollSensitivity:1,screenReaderMode:!1,smoothScrollDuration:0,macOptionIsMeta:!1,macOptionClickForcesSelection:!1,minimumContrastRatio:1,disableStdin:!1,allowProposedApi:!1,allowTransparency:!1,tabStopWidth:8,theme:{},rightClickSelectsWord:n.isMac,windowOptions:{},windowsMode:!1,windowsPty:{},wordSeparator:" ()[]{}',\"`",altClickMovesCursor:!0,convertEol:!1,termName:"xterm",cancelEvents:!1,overviewRulerWidth:0};const o=["normal","bold","100","200","300","400","500","600","700","800","900"];class a extends r.Disposable{constructor(e){super(),this._onOptionChange=this.register(new s.EventEmitter),this.onOptionChange=this._onOptionChange.event;const i=Object.assign({},t.DEFAULT_OPTIONS);for(const t in e)if(t in i)try{const s=e[t];i[t]=this._sanitizeAndValidateOption(t,s)}catch(e){console.error(e)}this.rawOptions=i,this.options=Object.assign({},i),this._setupOptions()}onSpecificOptionChange(e,t){return this.onOptionChange((i=>{i===e&&t(this.rawOptions[e])}))}onMultipleOptionChange(e,t){return this.onOptionChange((i=>{-1!==e.indexOf(i)&&t()}))}_setupOptions(){const e=e=>{if(!(e in t.DEFAULT_OPTIONS))throw new Error(`No option with key "${e}"`);return this.rawOptions[e]},i=(e,i)=>{if(!(e in t.DEFAULT_OPTIONS))throw new Error(`No option with key "${e}"`);i=this._sanitizeAndValidateOption(e,i),this.rawOptions[e]!==i&&(this.rawOptions[e]=i,this._onOptionChange.fire(e))};for(const t in this.rawOptions){const s={get:e.bind(this,t),set:i.bind(this,t)};Object.defineProperty(this.options,t,s)}}_sanitizeAndValidateOption(e,i){switch(e){case"cursorStyle":if(i||(i=t.DEFAULT_OPTIONS[e]),!function(e){return"block"===e||"underline"===e||"bar"===e}(i))throw new Error(`"${i}" is not a valid value for ${e}`);break;case"wordSeparator":i||(i=t.DEFAULT_OPTIONS[e]);break;case"fontWeight":case"fontWeightBold":if("number"==typeof i&&1<=i&&i<=1e3)break;i=o.includes(i)?i:t.DEFAULT_OPTIONS[e];break;case"cursorWidth":i=Math.floor(i);case"lineHeight":case"tabStopWidth":if(i<1)throw new Error(`${e} cannot be less than 1, value: ${i}`);break;case"minimumContrastRatio":i=Math.max(1,Math.min(21,Math.round(10*i)/10));break;case"scrollback":if((i=Math.min(i,4294967295))<0)throw new Error(`${e} cannot be less than 0, value: ${i}`);break;case"fastScrollSensitivity":case"scrollSensitivity":if(i<=0)throw new Error(`${e} cannot be less than or equal to 0, value: ${i}`);break;case"rows":case"cols":if(!i&&0!==i)throw new Error(`${e} must be numeric, value: ${i}`);break;case"windowsPty":i=null!=i?i:{}}return i}}t.OptionsService=a},2660:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.OscLinkService=void 0;const n=i(2585);let o=t.OscLinkService=class{constructor(e){this._bufferService=e,this._nextId=1,this._entriesWithId=new Map,this._dataByLinkId=new Map}registerLink(e){const t=this._bufferService.buffer;if(void 0===e.id){const i=t.addMarker(t.ybase+t.y),s={data:e,id:this._nextId++,lines:[i]};return i.onDispose((()=>this._removeMarkerFromLink(s,i))),this._dataByLinkId.set(s.id,s),s.id}const i=e,s=this._getEntryIdKey(i),r=this._entriesWithId.get(s);if(r)return this.addLineToLink(r.id,t.ybase+t.y),r.id;const n=t.addMarker(t.ybase+t.y),o={id:this._nextId++,key:this._getEntryIdKey(i),data:i,lines:[n]};return n.onDispose((()=>this._removeMarkerFromLink(o,n))),this._entriesWithId.set(o.key,o),this._dataByLinkId.set(o.id,o),o.id}addLineToLink(e,t){const i=this._dataByLinkId.get(e);if(i&&i.lines.every((e=>e.line!==t))){const e=this._bufferService.buffer.addMarker(t);i.lines.push(e),e.onDispose((()=>this._removeMarkerFromLink(i,e)))}}getLinkData(e){var t;return null===(t=this._dataByLinkId.get(e))||void 0===t?void 0:t.data}_getEntryIdKey(e){return`${e.id};;${e.uri}`}_removeMarkerFromLink(e,t){const i=e.lines.indexOf(t);-1!==i&&(e.lines.splice(i,1),0===e.lines.length&&(void 0!==e.data.id&&this._entriesWithId.delete(e.key),this._dataByLinkId.delete(e.id)))}};t.OscLinkService=o=s([r(0,n.IBufferService)],o)},8343:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.createDecorator=t.getServiceDependencies=t.serviceRegistry=void 0;const i="di$target",s="di$dependencies";t.serviceRegistry=new Map,t.getServiceDependencies=function(e){return e[s]||[]},t.createDecorator=function(e){if(t.serviceRegistry.has(e))return t.serviceRegistry.get(e);const r=function(e,t,n){if(3!==arguments.length)throw new Error("@IServiceName-decorator can only be used to decorate a parameter");!function(e,t,r){t[i]===t?t[s].push({id:e,index:r}):(t[s]=[{id:e,index:r}],t[i]=t)}(r,e,n)};return r.toString=()=>e,t.serviceRegistry.set(e,r),r}},2585:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.IDecorationService=t.IUnicodeService=t.IOscLinkService=t.IOptionsService=t.ILogService=t.LogLevelEnum=t.IInstantiationService=t.ICharsetService=t.ICoreService=t.ICoreMouseService=t.IBufferService=void 0;const s=i(8343);var r;t.IBufferService=(0,s.createDecorator)("BufferService"),t.ICoreMouseService=(0,s.createDecorator)("CoreMouseService"),t.ICoreService=(0,s.createDecorator)("CoreService"),t.ICharsetService=(0,s.createDecorator)("CharsetService"),t.IInstantiationService=(0,s.createDecorator)("InstantiationService"),function(e){e[e.TRACE=0]="TRACE",e[e.DEBUG=1]="DEBUG",e[e.INFO=2]="INFO",e[e.WARN=3]="WARN",e[e.ERROR=4]="ERROR",e[e.OFF=5]="OFF"}(r||(t.LogLevelEnum=r={})),t.ILogService=(0,s.createDecorator)("LogService"),t.IOptionsService=(0,s.createDecorator)("OptionsService"),t.IOscLinkService=(0,s.createDecorator)("OscLinkService"),t.IUnicodeService=(0,s.createDecorator)("UnicodeService"),t.IDecorationService=(0,s.createDecorator)("DecorationService")},1480:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.UnicodeService=void 0;const s=i(8460),r=i(225);t.UnicodeService=class{constructor(){this._providers=Object.create(null),this._active="",this._onChange=new s.EventEmitter,this.onChange=this._onChange.event;const e=new r.UnicodeV6;this.register(e),this._active=e.version,this._activeProvider=e}dispose(){this._onChange.dispose()}get versions(){return Object.keys(this._providers)}get activeVersion(){return this._active}set activeVersion(e){if(!this._providers[e])throw new Error(`unknown Unicode version "${e}"`);this._active=e,this._activeProvider=this._providers[e],this._onChange.fire(e)}register(e){this._providers[e.version]=e}wcwidth(e){return this._activeProvider.wcwidth(e)}getStringCellWidth(e){let t=0;const i=e.length;for(let s=0;s=i)return t+this.wcwidth(r);const n=e.charCodeAt(s);56320<=n&&n<=57343?r=1024*(r-55296)+n-56320+65536:t+=this.wcwidth(n)}t+=this.wcwidth(r)}return t}}}},t={};function i(s){var r=t[s];if(void 0!==r)return r.exports;var n=t[s]={exports:{}};return e[s].call(n.exports,n,n.exports,i),n.exports}var s={};return(()=>{var e=s;Object.defineProperty(e,"__esModule",{value:!0}),e.Terminal=void 0;const t=i(9042),r=i(3236),n=i(844),o=i(5741),a=i(8285),h=i(7975),c=i(7090),l=["cols","rows"];class d extends n.Disposable{constructor(e){super(),this._core=this.register(new r.Terminal(e)),this._addonManager=this.register(new o.AddonManager),this._publicOptions=Object.assign({},this._core.options);const t=e=>this._core.options[e],i=(e,t)=>{this._checkReadonlyOptions(e),this._core.options[e]=t};for(const e in this._core.options){const s={get:t.bind(this,e),set:i.bind(this,e)};Object.defineProperty(this._publicOptions,e,s)}}_checkReadonlyOptions(e){if(l.includes(e))throw new Error(`Option "${e}" can only be set in the constructor`)}_checkProposedApi(){if(!this._core.optionsService.rawOptions.allowProposedApi)throw new Error("You must set the allowProposedApi option to true to use proposed API")}get onBell(){return this._core.onBell}get onBinary(){return this._core.onBinary}get onCursorMove(){return this._core.onCursorMove}get onData(){return this._core.onData}get onKey(){return this._core.onKey}get onLineFeed(){return this._core.onLineFeed}get onRender(){return this._core.onRender}get onResize(){return this._core.onResize}get onScroll(){return this._core.onScroll}get onSelectionChange(){return this._core.onSelectionChange}get onTitleChange(){return this._core.onTitleChange}get onWriteParsed(){return this._core.onWriteParsed}get element(){return this._core.element}get parser(){return this._parser||(this._parser=new h.ParserApi(this._core)),this._parser}get unicode(){return this._checkProposedApi(),new c.UnicodeApi(this._core)}get textarea(){return this._core.textarea}get rows(){return this._core.rows}get cols(){return this._core.cols}get buffer(){return this._buffer||(this._buffer=this.register(new a.BufferNamespaceApi(this._core))),this._buffer}get markers(){return this._checkProposedApi(),this._core.markers}get modes(){const e=this._core.coreService.decPrivateModes;let t="none";switch(this._core.coreMouseService.activeProtocol){case"X10":t="x10";break;case"VT200":t="vt200";break;case"DRAG":t="drag";break;case"ANY":t="any"}return{applicationCursorKeysMode:e.applicationCursorKeys,applicationKeypadMode:e.applicationKeypad,bracketedPasteMode:e.bracketedPasteMode,insertMode:this._core.coreService.modes.insertMode,mouseTrackingMode:t,originMode:e.origin,reverseWraparoundMode:e.reverseWraparound,sendFocusMode:e.sendFocus,wraparoundMode:e.wraparound}}get options(){return this._publicOptions}set options(e){for(const t in e)this._publicOptions[t]=e[t]}blur(){this._core.blur()}focus(){this._core.focus()}resize(e,t){this._verifyIntegers(e,t),this._core.resize(e,t)}open(e){this._core.open(e)}attachCustomKeyEventHandler(e){this._core.attachCustomKeyEventHandler(e)}registerLinkProvider(e){return this._core.registerLinkProvider(e)}registerCharacterJoiner(e){return this._checkProposedApi(),this._core.registerCharacterJoiner(e)}deregisterCharacterJoiner(e){this._checkProposedApi(),this._core.deregisterCharacterJoiner(e)}registerMarker(e=0){return this._verifyIntegers(e),this._core.registerMarker(e)}registerDecoration(e){var t,i,s;return this._checkProposedApi(),this._verifyPositiveIntegers(null!==(t=e.x)&&void 0!==t?t:0,null!==(i=e.width)&&void 0!==i?i:0,null!==(s=e.height)&&void 0!==s?s:0),this._core.registerDecoration(e)}hasSelection(){return this._core.hasSelection()}select(e,t,i){this._verifyIntegers(e,t,i),this._core.select(e,t,i)}getSelection(){return this._core.getSelection()}getSelectionPosition(){return this._core.getSelectionPosition()}clearSelection(){this._core.clearSelection()}selectAll(){this._core.selectAll()}selectLines(e,t){this._verifyIntegers(e,t),this._core.selectLines(e,t)}dispose(){super.dispose()}scrollLines(e){this._verifyIntegers(e),this._core.scrollLines(e)}scrollPages(e){this._verifyIntegers(e),this._core.scrollPages(e)}scrollToTop(){this._core.scrollToTop()}scrollToBottom(){this._core.scrollToBottom()}scrollToLine(e){this._verifyIntegers(e),this._core.scrollToLine(e)}clear(){this._core.clear()}write(e,t){this._core.write(e,t)}writeln(e,t){this._core.write(e),this._core.write("\r\n",t)}paste(e){this._core.paste(e)}refresh(e,t){this._verifyIntegers(e,t),this._core.refresh(e,t)}reset(){this._core.reset()}clearTextureAtlas(){this._core.clearTextureAtlas()}loadAddon(e){this._addonManager.loadAddon(this,e)}static get strings(){return t}_verifyIntegers(...e){for(const t of e)if(t===1/0||isNaN(t)||t%1!=0)throw new Error("This API only accepts integers")}_verifyPositiveIntegers(...e){for(const t of e)if(t&&(t===1/0||isNaN(t)||t%1!=0||t<0))throw new Error("This API only accepts positive integers")}}e.Terminal=d})(),s})())); +//# sourceMappingURL=xterm.js.map \ No newline at end of file diff --git a/templates/cert.html b/templates/cert.html new file mode 100644 index 0000000..8c6a8b4 --- /dev/null +++ b/templates/cert.html @@ -0,0 +1,76 @@ + + + + + + Установка сертификата — AIS Map + + + + + +

Установка сертификата AIS Map

+ + Скачать сертификат (.crt) + +
+

Android

+
    +
  1. Нажмите кнопку выше — скачается файл
  2. +
  3. Откройте скачанный файл (или: Настройки → Безопасность → Установка сертификатов)
  4. +
  5. Выберите «CA-сертификат» / «Центр сертификации»
  6. +
  7. Подтвердите установку
  8. +
  9. Готово — перезагрузите страницу карты
  10. +
+
+ +
+

iPhone / iPad

+
    +
  1. Нажмите кнопку выше — откроется «Профиль загружен»
  2. +
  3. Настройки → Основные → Профили → «AIS Map Root CA» → Установить
  4. +
  5. Настройки → Основные → Об этом устройстве → Доверие сертификатам → включите «AIS Map Root CA»
  6. +
  7. Готово — перезагрузите страницу карты
  8. +
+
+ +
+

Firefox (ПК / Android)

+
    +
  1. Откройте /ca.pem
  2. +
  3. Firefox предложит импортировать — отметьте «Доверять для идентификации сайтов»
  4. +
  5. Нажмите ОК
  6. +
+
+ +
+

Chrome / Edge (ПК)

+
    +
  1. Скачайте /ca.crt
  2. +
  3. Настройки → Конфиденциальность и безопасность → Безопасность → Управление сертификатами
  4. +
  5. Вкладка «Доверенные корневые центры» → Импорт → выберите файл
  6. +
+
+ +

После установки предупреждение о безопасности больше не появится.
+ Сертификат действителен 10 лет и работает только в вашей локальной сети.

+ + ← Вернуться к карте + + diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..ba64905 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,700 @@ + + + + + +AIS Map v2 + + + + + + + + + + +
+ +
+ + +
+
Ожидание данных AIS...
+ +
Координаты: -
+
+

Своё судно

+
Источник: -
+
Координаты: -
+
Курс: -
+
Скорость: -
+
Спутники: -
+
+ Компас: + + + + + N + + +
+
Источник GPS и «Следовать» — в панели управления картой
+
+
+ + + + + + + + + + + +
+
+
+ + + + +
+
SOG
+
COG
+
+
+
+
+
+
+ +
+ + + +
+ + +
+
+

Цели (0)

+
+ +
+ + + +
+
+
+
+
+ + +
+
+
0
Активных целей
+
0
Всего за сессию
+
0
Класс A
+
0
Класс B
+
0
AIS сообщений
+
0
GPS сообщений
+
-
GPS фикс
+
-
Аптайм приложения
+
-
Аптайм системы
+
-
Время приёмника
+
-
Температура CPU
+
-
Загрузка CPU
+
-
Память
+
-
AIS поток
+
-
Все NMEA
+
0
Тест (247320161)
A: 0 B: 0
+
+
+

AIS сообщения по типам

+ + + +
ТипОписаниеКоличество
+
+
+
+ Расширенная статистика (ошибки/парсинг) +
+
+
Ошибки парсинга
+ + +
+
+
+
UDP / фрагменты
+ + +
+
+
+
Хранилище / отправка
+ + +
+
+
+
WS / прочее
+ + +
+
+
+
+
+
+
+ Слоты TDMA +
+
+
+
+
Канал A
+
Нет данных
+ + + +
+
+
Канал B
+
Нет данных
+ + + +
+
+
+ Свободен + Занят + RSSI + Event + Кадр + Noise Floor + Threshold +
+
+ Тест слота: + + + + +
+
Клик по слоту покажет детали.
+
+
+
+
+ + +
+ + +
+

Сеть / Режим работы

+
+ Текущий режим + ... +
+
+ IP-адрес устройства + - +
+
+ SSID + - +
+ +
+
Точка доступа (AP)
+
WiFi-клиент
+
+ + + + + + + +
+ + +
+
+
+ +
+

Подключение

+
UDP порт (AIS + GPS)5006
+
Web-сервер-
+
HTTPS-
+
+
+

Тайминги целей

+
+ Потеря цели (подсветка), мин + + + - + +
+
+ Удаление цели, мин + + + - + +
+
+
+

Единицы измерения

+
+ Расстояние + +
+
+ Скорость + +
+
+
+

Радиусы пеленга

+
+ Радиус 1 (ВНИМАНИЕ), NM + + + + +
+
+ Радиус 2 (подсветка), NM + + + + +
+

Окружности рисуются вокруг “Своё судно”. Радиус 1 включает баннер «ВНИМАНИЕ», радиус 2 подсвечивает близкие цели.

+
+
+

Сертификат

+ +
+
+

О системе

+
Версия1.0
+
Secure context-
+
+
+ + +
+
+

Наше судно (Class B / B+)

+

MMSI, имя, позывной, габариты и данные для сообщений 18, 19, 24. Движение из GPS при включённой опции ниже.

+
MMSI
+
Имя судна (до 20)
+
Позывной
+
Тип судна + +
+

Табл. 51: ОПГ — опасные грузы; ОН — опасные только насыпью; ВВ — вредные вещества; МЗ — морские загрязнители. Категории X, Y, Z, OS — по ИМО (ранее A, B, C, D).

+
Габариты: A B C D (м) + + A нос + + B корма + + C порт + + D штб + + +
+
+

Мышью: корма — L, правый борт — W (доли A/L и C/W как в начале жеста), GPS — сдвиг внутри L×W. Увеличивать W/L можно и за пределами контура — координаты не обрезаются по краю корпуса. Точные A–D — в полях выше.

+
+ + + + + + + + + + + + + нос (курс) + корма + + + + GPS + + + + +
    +
  • Серые размеры — W сверху, L справа
  • +
  • Жёлтые маркеры — корма = L, правый борт = W (A–D пересчитываются пропорционально)
  • +
  • GPS — перетаскивание на схеме или спинбоксы A–D
  • +
  • A — нос → GPS (бит 21–29)
  • +
  • B — GPS → корма (12–20)
  • +
  • C — порт → GPS (6–11)
  • +
  • D — GPS → штюрборд (0–5)
  • +
+
+
+
Vendor ID (6)
+
Модель / серийный № + + + + +
+
Движение из GPS
+
GPS (ownship)
+
+
+

Отправка NRZI

+

В UDP 127.0.0.1:6010 уходит пакет как в «Тест слота»: канал (1 байт) + слот (uint16 LE) + сгенерированный NRZI.

+
Канал / слот + + + + +
+
Кодер NRZI + +
+
NRZI упаковка + +
+
Преамбула NRZI
+
Длина преамбулы (бит)
+
Добор payload до октета
+
Добор NRZ до (бит)
+
Импульс TX (GPIO) + + + +
+
Пауза перед GPIO (мс)
+
Скрипт импульса
+
+ + + + + + + + +
+
+
+
+

Сырой NRZI (hex)

+

Вставьте байты в hex (пробелы допускаются). Отправка: те же канал и слот, что выше → UDP 127.0.0.1:6010 как у теста слота. При включённой галочке «после UDP» к телу запроса подмешиваются настройки GPIO из формы.

+ +
+ +
+
+
+

Превью (чистый NRZI и полный UDP-кадр)

+
Нажмите «Обновить превью».
+
+
+ + +
+
+ + + + Автопрокрутка + Очистить + + 0 строк +
+
+
+ + +
+
+ Конфиги + + Сервис: + ... +
+
+
+ + + + Файл: /ais-mini.conf +
+ +
+ + + + + +
+
Файл: /ais-mini.conf  |  Сервис: aisMini.service
+
+
+ + +
+
+ Локальная оболочка на устройстве + + +
+
+
+
+ + + + + + + + + + + diff --git a/terminal.py b/terminal.py new file mode 100644 index 0000000..9878d31 --- /dev/null +++ b/terminal.py @@ -0,0 +1,92 @@ +import os +import json +import struct +import signal +import select +import threading + + +def _pty_set_winsize(fd, rows, cols): + import fcntl + import termios + + winsz = struct.pack("HHHH", int(rows), int(cols), 0, 0) + fcntl.ioctl(fd, termios.TIOCSWINSZ, winsz) + + +def terminal_session(ws): + """PTY + bash, обмен через WebSocket (как ttyd, но на том же порту что и Flask).""" + import pty + + pid, master_fd = pty.fork() + if pid == 0: + os.environ.setdefault("TERM", "xterm-256color") + os.environ.setdefault("SHELL", "/bin/bash") + try: + os.execvp("/bin/bash", ["/bin/bash"]) + except OSError: + os.execvp("/bin/sh", ["/bin/sh"]) + os._exit(127) + + relay_done = threading.Event() + ws_lock = threading.Lock() + + def relay_out(): + try: + while not relay_done.is_set(): + r, _, _ = select.select([master_fd], [], [], 0.25) + if master_fd not in r: + continue + try: + chunk = os.read(master_fd, 65536) + except OSError: + break + if not chunk: + break + try: + with ws_lock: + ws.send(chunk) + except Exception: + break + finally: + relay_done.set() + + t = threading.Thread(target=relay_out, daemon=True) + t.start() + + try: + while True: + msg = ws.receive() + if msg is None: + break + if isinstance(msg, str) and msg.startswith("{"): + try: + j = json.loads(msg) + if j.get("type") == "resize": + r, c = int(j.get("rows") or 24), int(j.get("cols") or 80) + if r > 0 and c > 0: + _pty_set_winsize(master_fd, r, c) + continue + except (json.JSONDecodeError, TypeError, ValueError, KeyError): + pass + if isinstance(msg, str): + msg = msg.encode("utf-8", errors="replace") + try: + os.write(master_fd, msg) + except OSError: + break + finally: + relay_done.set() + try: + os.close(master_fd) + except OSError: + pass + try: + os.kill(pid, signal.SIGTERM) + except ProcessLookupError: + pass + try: + os.waitpid(pid, 0) + except ChildProcessError: + pass + t.join(timeout=1.5) diff --git a/tools/build_mmsi_mid_json.py b/tools/build_mmsi_mid_json.py new file mode 100644 index 0000000..a8d8902 --- /dev/null +++ b/tools/build_mmsi_mid_json.py @@ -0,0 +1,174 @@ +"""Build static/js/mmsi_mid_iso2.json from Wikipedia MID table (embedded). Run: python tools/build_mmsi_mid_json.py""" +import json +import re + +# Fragment of https://en.wikipedia.org/wiki/Maritime_identification_digits (action=raw), table rows only +WIKI_TABLE = r""" +| [Russian Federation](https://en.wikipedia.org/wiki/Russia) | 273 | +| [Latvia](https://en.wikipedia.org/wiki/Latvia)(Republic of) | 275 | +| [Lithuania](https://en.wikipedia.org/wiki/Lithuania)(Republic of) | 277 | +| [Estonia](https://en.wikipedia.org/wiki/Estonia)(Republic of) | 276 | +| [Ukraine](https://en.wikipedia.org/wiki/Ukraine) | 272 | +| [Poland](https://en.wikipedia.org/wiki/Poland)(Republic of) | 261 | +| [Germany](https://en.wikipedia.org/wiki/Germany)(Federal Republic of) | 211; 218 | +| [France](https://en.wikipedia.org/wiki/France) | 226; 227; 228 | +| [Italy](https://en.wikipedia.org/wiki/Italy) | 247 | +| [Norway](https://en.wikipedia.org/wiki/Norway) | 257; 258; 259 | +| [Sweden](https://en.wikipedia.org/wiki/Sweden) | 265; 266 | +| [Finland](https://en.wikipedia.org/wiki/Finland) | 230 | +| [Denmark](https://en.wikipedia.org/wiki/Denmark) | 219; 220 | +| [Netherlands](https://en.wikipedia.org/wiki/Netherlands)(Kingdom of the) | 244; 245; 246 | +| [Belgium](https://en.wikipedia.org/wiki/Belgium) | 205 | +| [Spain](https://en.wikipedia.org/wiki/Spain) | 224; 225 | +| [Greece](https://en.wikipedia.org/wiki/Greece) | 237; 239; 240; 241 | +| [Turkey](https://en.wikipedia.org/wiki/Turkey) | 271 | +| [Romania](https://en.wikipedia.org/wiki/Romania) | 264 | +| [Bulgaria](https://en.wikipedia.org/wiki/Bulgaria)(Republic of) | 207 | +| [Croatia](https://en.wikipedia.org/wiki/Croatia)(Republic of) | 238 | +| [United Kingdom of Great Britain and Northern Ireland](https://en.wikipedia.org/wiki/United_Kingdom) | 232; 233; 234; 235 | +| [Ireland](https://en.wikipedia.org/wiki/Republic_of_Ireland) | 250 | +| [Iceland](https://en.wikipedia.org/wiki/Iceland) | 251 | +| [Malta](https://en.wikipedia.org/wiki/Malta) | 215; 229; 248; 249; 256 | +| [Cyprus](https://en.wikipedia.org/wiki/Cyprus)(Republic of) | 209; 210; 212 | +| [United States of America](https://en.wikipedia.org/wiki/United_States) | 338; 366; 367; 368; 369 | +| [Canada](https://en.wikipedia.org/wiki/Canada) | 316 | +| [China](https://en.wikipedia.org/wiki/China)(People's Republic of) | 412; 413; 414 | +| [Japan](https://en.wikipedia.org/wiki/Japan) | 431; 432 | +| [Korea](https://en.wikipedia.org/wiki/South_Korea)(Republic of) | 440; 441 | +| [Singapore](https://en.wikipedia.org/wiki/Singapore)(Republic of) | 563; 564; 565; 566 | +| [India](https://en.wikipedia.org/wiki/India)(Republic of) | 419 | +| [Australia](https://en.wikipedia.org/wiki/Australia) | 503 | +| [Brazil](https://en.wikipedia.org/wiki/Brazil)(Federative Republic of) | 710 | +| [Argentina](https://en.wikipedia.org/wiki/Argentina) | 701 | +| [Panama](https://en.wikipedia.org/wiki/Panama)(Republic of) | 351; 352; 353; 354; 355; 356; 357; 370; 371; 372; 373; 374 | +| [Liberia](https://en.wikipedia.org/wiki/Liberia)(Republic of) | 636; 637 | +| [Marshall Islands](https://en.wikipedia.org/wiki/Marshall_Islands)(Republic of the) | 538 | +| [Bahamas](https://en.wikipedia.org/wiki/The_Bahamas)(Commonwealth of the) | 308; 309; 311 | +| [Hong Kong](https://en.wikipedia.org/wiki/Hong_Kong)(Special Administrative Region of China) | 477 | +| [Taiwan](https://en.wikipedia.org/wiki/Taiwan)(Republic of China) | 416 | +| [Vietnam](https://en.wikipedia.org/wiki/Vietnam)(Socialist Republic of) | 574 | +| [Indonesia](https://en.wikipedia.org/wiki/Indonesia)(Republic of) | 525 | +| [Malaysia](https://en.wikipedia.org/wiki/Malaysia) | 533 | +| [Philippines](https://en.wikipedia.org/wiki/Philippines)(Republic of the) | 548 | +| [Thailand](https://en.wikipedia.org/wiki/Thailand) | 567 | +| [Saudi Arabia](https://en.wikipedia.org/wiki/Saudi_Arabia)(Kingdom of) | 403 | +| [United Arab Emirates](https://en.wikipedia.org/wiki/United_Arab_Emirates) | 470; 471 | +| [Israel](https://en.wikipedia.org/wiki/Israel)(State of) | 428 | +| [Egypt](https://en.wikipedia.org/wiki/Egypt)(Arab Republic of) | 622 | +| [South Africa](https://en.wikipedia.org/wiki/South_Africa)(Republic of) | 601 | +| [New Zealand](https://en.wikipedia.org/wiki/New_Zealand) | 512 | +| [Mexico](https://en.wikipedia.org/wiki/Mexico) | 345 | +| [Chile](https://en.wikipedia.org/wiki/Chile) | 725 | +| [Portugal](https://en.wikipedia.org/wiki/Portugal) | 263 | +| [Switzerland](https://en.wikipedia.org/wiki/Switzerland)(Confederation of) | 269 | +| [Austria](https://en.wikipedia.org/wiki/Austria) | 203 | +| [Czech Republic](https://en.wikipedia.org/wiki/Czech_Republic) | 270 | +| [Hungary](https://en.wikipedia.org/wiki/Hungary)(Republic of) | 243 | +| [Serbia](https://en.wikipedia.org/wiki/Serbia) | 279 | +| [Slovenia](https://en.wikipedia.org/wiki/Slovenia)(Republic of) | 278 | +| [Slovakia](https://en.wikipedia.org/wiki/Slovakia) | 267 | +| [Montenegro](https://en.wikipedia.org/wiki/Montenegro)(Republic of) | 262 | +| [Albania](https://en.wikipedia.org/wiki/Albania)(Republic of) | 201 | +| [Georgia](https://en.wikipedia.org/wiki/Georgia_(country)) | 213 | +| [Kazakhstan](https://en.wikipedia.org/wiki/Kazakhstan)(Republic of) | 436 | +| [Iran](https://en.wikipedia.org/wiki/Iran)(Islamic Republic of) | 422 | +""" + +NAME_TO_ISO2 = { + "Russian Federation": "RU", + "Latvia": "LV", + "Lithuania": "LT", + "Estonia": "EE", + "Ukraine": "UA", + "Poland": "PL", + "Germany": "DE", + "France": "FR", + "Italy": "IT", + "Norway": "NO", + "Sweden": "SE", + "Finland": "FI", + "Denmark": "DK", + "Netherlands": "NL", + "Belgium": "BE", + "Spain": "ES", + "Greece": "GR", + "Turkey": "TR", + "Romania": "RO", + "Bulgaria": "BG", + "Croatia": "HR", + "United Kingdom of Great Britain and Northern Ireland": "GB", + "Ireland": "IE", + "Iceland": "IS", + "Malta": "MT", + "Cyprus": "CY", + "United States of America": "US", + "Canada": "CA", + "China": "CN", + "Japan": "JP", + "Korea": "KR", + "Singapore": "SG", + "India": "IN", + "Australia": "AU", + "Brazil": "BR", + "Argentina": "AR", + "Panama": "PA", + "Liberia": "LR", + "Marshall Islands": "MH", + "Bahamas": "BS", + "Hong Kong": "HK", + "Taiwan": "TW", + "Vietnam": "VN", + "Indonesia": "ID", + "Malaysia": "MY", + "Philippines": "PH", + "Thailand": "TH", + "Saudi Arabia": "SA", + "United Arab Emirates": "AE", + "Israel": "IL", + "Egypt": "EG", + "South Africa": "ZA", + "New Zealand": "NZ", + "Mexico": "MX", + "Chile": "CL", + "Portugal": "PT", + "Switzerland": "CH", + "Austria": "AT", + "Czech Republic": "CZ", + "Hungary": "HU", + "Serbia": "RS", + "Slovenia": "SI", + "Slovakia": "SK", + "Montenegro": "ME", + "Albania": "AL", + "Georgia": "GE", + "Kazakhstan": "KZ", + "Iran": "IR", +} + + +def main(): + pat = re.compile(r"\| \[([^\]]+)\]\([^\)]+\)[^\|]*\| ([0-9][^|\n]*) \|") + mid_to_iso = {} + for m in pat.finditer(WIKI_TABLE): + name = m.group(1).strip() + codes_cell = m.group(2) + codes = [ + x.strip() + for x in codes_cell.replace(";", " ").split() + if x.strip().isdigit() and len(x.strip()) == 3 + ] + iso = NAME_TO_ISO2.get(name) + if not iso: + raise SystemExit(f"Missing ISO2 for country: {name!r}") + for mid in codes: + mid_to_iso[mid] = iso + from pathlib import Path + root = Path(__file__).resolve().parent.parent + out = root / "static" / "js" / "mmsi_mid_iso2.json" + with open(out, "w", encoding="utf-8") as f: + json.dump(mid_to_iso, f, separators=(",", ":")) + print("Wrote", out, len(mid_to_iso), "MIDs") + + +if __name__ == "__main__": + main() diff --git a/transponder.py b/transponder.py new file mode 100644 index 0000000..45c300d --- /dev/null +++ b/transponder.py @@ -0,0 +1,664 @@ +""" +Настройки «нашего судна» для Class B / B+ и сборка AIS-сообщений 18, 19, 24. +""" +from __future__ import annotations + +import json +import os +import re +import socket +import struct +import subprocess +import sys +import threading +import time +from typing import Any, Dict, List, Optional, Tuple + +from bitarray import bitarray + +try: + from pyais.messages import MSG_CLASS +except ImportError: + MSG_CLASS = {} +try: + from pyais.constants import EpfdType +except ImportError: + EpfdType = None # type: ignore + +from ais_phy import ( + ais_channel_to_nrzi_bytes, + hdlc_nrzi_bytes_no_preamble, + phy_frame_bit_counts, +) + +try: + from ais_nrzi_pipeline import phy as _aistx_phy # type: ignore +except ImportError: + _aistx_phy = None + + +def is_aistx_phy_available() -> bool: + return _aistx_phy is not None + + +def _expand_nrzi_packed_msb(packed: bytes) -> bytes: + """Один байт NRZI → 8 байт 0x00/0xFF (MSB первый).""" + out = bytearray() + for byte in packed: + for i in range(7, -1, -1): + out.append(0xFF if (byte >> i) & 1 else 0x00) + return bytes(out) + +CONFIG_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "transponder_config.json") +_config_lock = threading.Lock() + +# Фиксированная доставка NRZI (как тестовый слот / приёмник на устройстве). +NRZI_UDP_HOST = "127.0.0.1" +NRZI_UDP_PORT = 6010 + +DEFAULT_TRANSPONDER: Dict[str, Any] = { + "mmsi": 123456789, + "shipname": "MYSHIP", + "callsign": "CALLSG", + "ship_type": 37, + "to_bow": 10, + "to_stern": 10, + "to_port": 3, + "to_starboard": 3, + "vendorid": "1HZZZZ", + "model": 1, + "serial": 1, + "nrzi_mode": "packed", + # По умолчанию gr-aistx-цепочка; ais_phy — альтернатива (pyais CRC без reverse-byte). + "nrzi_encoder": "aistx", + "nrzi_preamble_bits": 24, + "include_nrzi_preamble": True, + # Как в GNU Radio: выравнивание payload до октета нулями; добор NRZ до N бит перед NRZI. + "nrzi_pad_payload_to_octet": True, + # GNU Radio часто 200; с преамбулой 24+HDLC NRZ обычно >200 бит — добор до 200 не меняет поток. + "nrzi_pad_nrz_bits": 256, + "use_gps_motion": True, + # Заголовок UDP как у «Тест слота»: 1 байт канал + uint16 LE слот + NRZI + "nrzi_slot_channel": "A", + "nrzi_slot": 0, + # После UDP: пауза 50–100 ms, затем запуск скрипта (напр. pulse_once.py на Orange Pi). + "tx_gpio_pulse_auto": False, + "tx_gpio_pulse_script": "", + "tx_gpio_pulse_delay_ms": 75, +} + + +def build_slot_udp_payload(channel: str, slot: int, nrzi_body: bytes) -> bytes: + """Тот же формат, что /api/send_test_slot: ch(0=A,1=B) + slot + данные.""" + ch = str(channel).strip().upper() + ch_byte = b"\x00" if ch == "A" else b"\x01" + try: + s = int(slot) + except (TypeError, ValueError): + s = 0 + s = max(0, min(2249, s)) + return ch_byte + struct.pack(" Dict[str, Any]: + with _config_lock: + if not os.path.exists(CONFIG_PATH): + return dict(DEFAULT_TRANSPONDER) + try: + with open(CONFIG_PATH, "r", encoding="utf-8") as f: + data = json.load(f) + out = dict(DEFAULT_TRANSPONDER) + for k, v in data.items(): + if k in DEFAULT_TRANSPONDER: + out[k] = v + return out + except (OSError, json.JSONDecodeError): + return dict(DEFAULT_TRANSPONDER) + + +def save_transponder_config(cfg: Dict[str, Any]) -> Dict[str, Any]: + merged = normalize_transponder_config(cfg) + with _config_lock: + with open(CONFIG_PATH, "w", encoding="utf-8") as f: + json.dump(merged, f, indent=2, ensure_ascii=False) + return merged + + +def normalize_transponder_config(cfg: Dict[str, Any]) -> Dict[str, Any]: + out = dict(DEFAULT_TRANSPONDER) + for k in DEFAULT_TRANSPONDER: + if k not in cfg: + continue + v = cfg[k] + if k in ( + "mmsi", + "ship_type", + "to_bow", + "to_stern", + "to_port", + "to_starboard", + "model", + "serial", + ): + try: + out[k] = int(v) + except (TypeError, ValueError): + out[k] = DEFAULT_TRANSPONDER[k] + elif k in ("use_gps_motion", "include_nrzi_preamble"): + if isinstance(v, str): + out[k] = v.strip().lower() in ("1", "true", "yes", "on") + else: + out[k] = bool(v) + elif k == "nrzi_mode": + s = str(v).lower() + out[k] = s if s in ("packed", "expanded") else "packed" + elif k == "nrzi_encoder": + s = str(v).strip().lower() + if s == "aistx" and _aistx_phy is None: + out[k] = "ais_phy" + else: + out[k] = s if s in ("ais_phy", "aistx") else DEFAULT_TRANSPONDER[k] + elif k == "nrzi_slot_channel": + s = str(v).strip().upper() + out[k] = s if s in ("A", "B") else DEFAULT_TRANSPONDER[k] + elif k == "nrzi_slot": + try: + si = int(v) + out[k] = max(0, min(2249, si)) + except (TypeError, ValueError): + out[k] = DEFAULT_TRANSPONDER[k] + elif k == "nrzi_preamble_bits": + try: + n = int(v) + out[k] = max(0, min(128, n)) + except (TypeError, ValueError): + out[k] = DEFAULT_TRANSPONDER[k] + elif k == "nrzi_pad_nrz_bits": + try: + n = int(v) + out[k] = max(0, min(4096, n)) + except (TypeError, ValueError): + out[k] = DEFAULT_TRANSPONDER[k] + elif k == "nrzi_pad_payload_to_octet": + if isinstance(v, str): + out[k] = v.strip().lower() in ("1", "true", "yes", "on") + else: + out[k] = bool(v) + elif k in ("shipname", "callsign", "vendorid", "tx_gpio_pulse_script"): + out[k] = str(v) if v is not None else DEFAULT_TRANSPONDER[k] + elif k == "tx_gpio_pulse_auto": + if isinstance(v, str): + out[k] = v.strip().lower() in ("1", "true", "yes", "on") + else: + out[k] = bool(v) + elif k == "tx_gpio_pulse_delay_ms": + try: + n = int(v) + out[k] = max(50, min(100, n)) + except (TypeError, ValueError): + out[k] = DEFAULT_TRANSPONDER[k] + else: + out[k] = v + return out + + +def merge_transponder_request( + base: Dict[str, Any], body: Optional[Dict[str, Any]] +) -> Dict[str, Any]: + merged = dict(base) + if body: + for k in DEFAULT_TRANSPONDER: + if k in body: + merged[k] = body[k] + return normalize_transponder_config(merged) + + +def _second_field() -> int: + return int(time.gmtime(time.time()).tm_sec) % 60 + + +def _motion_from_ownship(cfg: Dict[str, Any], own: Dict[str, Any]) -> Tuple[float, float, float, float, int]: + lat = float(own.get("lat") or 0.0) + lon = float(own.get("lon") or 0.0) + speed = float(own.get("speed") or 0.0) if cfg.get("use_gps_motion", True) else 0.0 + course = float(own.get("course") or 0.0) if cfg.get("use_gps_motion", True) else 0.0 + heading = int(own.get("heading") or own.get("course") or 0) if cfg.get("use_gps_motion", True) else 0 + heading = max(0, min(511, heading)) + return lat, lon, speed, course, heading + + +def build_type18_dict(cfg: Dict[str, Any], own: Dict[str, Any]) -> Dict[str, Any]: + lat, lon, speed, course, heading = _motion_from_ownship(cfg, own) + return { + "msg_type": 18, + "repeat": 0, + "mmsi": int(cfg["mmsi"]), + "lat": lat, + "lon": lon, + "speed": speed, + "course": course, + "heading": heading, + "second": _second_field(), + } + + +def build_type19_dict(cfg: Dict[str, Any], own: Dict[str, Any]) -> Dict[str, Any]: + lat, lon, speed, course, heading = _motion_from_ownship(cfg, own) + epfd = EpfdType.GPS if EpfdType is not None else 1 + return { + "msg_type": 19, + "repeat": 0, + "mmsi": int(cfg["mmsi"]), + "lat": lat, + "lon": lon, + "speed": speed, + "course": course, + "heading": heading, + "second": _second_field(), + "shipname": str(cfg.get("shipname") or "")[:20], + "ship_type": int(cfg.get("ship_type") or 0), + "to_bow": int(cfg.get("to_bow") or 0), + "to_stern": int(cfg.get("to_stern") or 0), + "to_port": int(cfg.get("to_port") or 0), + "to_starboard": int(cfg.get("to_starboard") or 0), + "epfd": epfd, + "raim": False, + "dte": False, + "assigned": False, + } + + +def build_type24a_dict(cfg: Dict[str, Any]) -> Dict[str, Any]: + return { + "msg_type": 24, + "repeat": 0, + "partno": 0, + "mmsi": int(cfg["mmsi"]), + "shipname": str(cfg.get("shipname") or "")[:20], + } + + +def build_type24b_dict(cfg: Dict[str, Any]) -> Dict[str, Any]: + vid = str(cfg.get("vendorid") or "")[:6] + if len(vid) < 6: + vid = (vid + "@@@@@@")[:6] + return { + "msg_type": 24, + "repeat": 0, + "partno": 1, + "mmsi": int(cfg["mmsi"]), + "ship_type": int(cfg.get("ship_type") or 0), + "vendorid": vid, + "model": int(cfg.get("model") or 0), + "serial": int(cfg.get("serial") or 0), + "callsign": str(cfg.get("callsign") or "")[:7], + "to_bow": int(cfg.get("to_bow") or 0), + "to_stern": int(cfg.get("to_stern") or 0), + "to_port": int(cfg.get("to_port") or 0), + "to_starboard": int(cfg.get("to_starboard") or 0), + } + + +def payload_bits_from_dict( + data: Dict[str, Any], *, pad_payload_to_octet: bool = False +) -> bitarray: + if not MSG_CLASS: + raise RuntimeError("pyais не установлен") + raw_t = data.get("msg_type", data.get("type")) + if raw_t is None: + raise ValueError("В данных AIS нужен msg_type (или устаревшее type)") + ais_type = int(raw_t) + # pyais Payload.create игнорирует неизвестные ключи; type/msg_type не являются полями attrs. + create_kw = {k: v for k, v in data.items() if k not in ("type", "msg_type")} + payload = MSG_CLASS[ais_type].create(**create_kw) + bits = payload.to_bitarray() + if len(bits) % 8 != 0 and not pad_payload_to_octet: + raise ValueError(f"Payload type {ais_type} has length {len(bits)} not multiple of 8") + return bits + + +def nrzi_bytes_for_payload( + data: Dict[str, Any], + mode: str, + *, + preamble_bits: int = 24, + pad_payload_to_octet: bool = False, + pad_nrz_total_bits: Optional[int] = None, +) -> bytes: + bits = payload_bits_from_dict(data, pad_payload_to_octet=pad_payload_to_octet) + return ais_channel_to_nrzi_bytes( + bits, + preamble_bits=preamble_bits, + nrzi_byte_mode=mode, + pad_payload_to_octet=pad_payload_to_octet, + pad_nrz_total_bits=pad_nrz_total_bits, + ) + + +def nrzi_bytes_hdlc_only( + data: Dict[str, Any], + mode: str, + *, + pad_payload_to_octet: bool = False, + pad_nrz_total_bits: Optional[int] = None, +) -> bytes: + bits = payload_bits_from_dict(data, pad_payload_to_octet=pad_payload_to_octet) + return hdlc_nrzi_bytes_no_preamble( + bits, + nrzi_byte_mode=mode, + pad_payload_to_octet=pad_payload_to_octet, + pad_nrz_total_bits=pad_nrz_total_bits, + ) + + +def _encode_nrzi_for_dict( + data: Dict[str, Any], + cfg: Dict[str, Any], + *, + include_preamble: bool, + mode: str, + pre_bits: int, + pad_oct: bool, + pad_nrz: Optional[int], +) -> bytes: + enc = str(cfg.get("nrzi_encoder") or DEFAULT_TRANSPONDER["nrzi_encoder"]).strip().lower() + if enc == "aistx" and _aistx_phy is not None: + bits = payload_bits_from_dict(data, pad_payload_to_octet=pad_oct) + if include_preamble: + fb = _aistx_phy.build_nrzi_frame(bits.to01(), enable_nrzi=True) + raw = _aistx_phy.nrzi_bits_to_bytes(fb) + if mode == "expanded": + raw = _expand_nrzi_packed_msb(raw) + return raw + return nrzi_bytes_hdlc_only( + data, + mode, + pad_payload_to_octet=pad_oct, + pad_nrz_total_bits=pad_nrz, + ) + if include_preamble: + return nrzi_bytes_for_payload( + data, + mode, + preamble_bits=pre_bits, + pad_payload_to_octet=pad_oct, + pad_nrz_total_bits=pad_nrz, + ) + return nrzi_bytes_hdlc_only( + data, + mode, + pad_payload_to_octet=pad_oct, + pad_nrz_total_bits=pad_nrz, + ) + + +def parse_nrzi_hex(hex_text: str) -> bytes: + s = re.sub(r"[\s:,\n\r\t]+", "", (hex_text or "").strip()) + if not s: + raise ValueError("Пустая hex-строка") + if len(s) % 2: + raise ValueError("Hex должен содержать чётное число символов") + try: + return bytes.fromhex(s) + except ValueError as e: + raise ValueError(f"Некорректный hex: {e}") from e + + +def _nrzi_pad_opts(cfg: Dict[str, Any]) -> Tuple[bool, Optional[int]]: + pad_oct = bool(cfg.get("nrzi_pad_payload_to_octet", True)) + try: + n = int(cfg.get("nrzi_pad_nrz_bits", 256)) + except (TypeError, ValueError): + n = 256 + pad_nrz = None if n <= 0 else max(1, min(4096, n)) + return pad_oct, pad_nrz + + +def _send_udp(host: str, port: int, data: bytes) -> None: + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + try: + sock.sendto(data, (host, port)) + finally: + sock.close() + + +def tx_gpio_pulse( + cfg: Dict[str, Any], *, after_udp: bool +) -> Dict[str, Any]: + """ + after_udp=True: только если tx_gpio_pulse_auto; пауза 50–100 ms после UDP, затем скрипт. + after_udp=False: ручной импульс (без паузы, без проверки auto). + """ + if after_udp and not bool(cfg.get("tx_gpio_pulse_auto")): + return {"skipped": True} + script = str(cfg.get("tx_gpio_pulse_script") or "").strip() + if not script: + if after_udp: + return {"skipped": True, "reason": "no_script"} + return {"ok": False, "error": "Не задан путь к скрипту импульса TX"} + if not os.path.isfile(script): + err = f"Файл не найден: {script}" + if after_udp: + return {"ok": False, "error": err} + return {"ok": False, "error": err} + if after_udp: + try: + ms = int(cfg.get("tx_gpio_pulse_delay_ms", 75)) + except (TypeError, ValueError): + ms = 75 + ms = max(50, min(100, ms)) + time.sleep(ms / 1000.0) + try: + r = subprocess.run( + [sys.executable, script], + capture_output=True, + text=True, + timeout=15, + ) + out: Dict[str, Any] = { + "ok": r.returncode == 0, + "returncode": r.returncode, + } + if r.stderr and r.stderr.strip(): + out["stderr"] = r.stderr.strip()[:500] + if r.stdout and r.stdout.strip(): + out["stdout"] = r.stdout.strip()[:300] + if r.returncode != 0 and not out.get("stderr"): + out["error"] = f"exit {r.returncode}" + return out + except subprocess.TimeoutExpired: + return {"ok": False, "error": "GPIO script timeout"} + except OSError as e: + return {"ok": False, "error": str(e)} + + +def _append_gpio_result(sent: Dict[str, Any], pulse: Dict[str, Any]) -> None: + if pulse.get("skipped"): + return + lst: List[Dict[str, Any]] = sent.setdefault("gpio_pulse", []) # type: ignore[assignment] + lst.append(pulse) + if not pulse.get("ok"): + err = pulse.get("error") or pulse.get("stderr") or f"code {pulse.get('returncode')}" + sent["errors"].append(f"GPIO TX: {err}") + + +def send_raw_nrzi_packet( + channel: str, slot: int, nrzi_body: bytes, *, cfg: Optional[Dict[str, Any]] = None +) -> Dict[str, Any]: + """UDP: канал + слот + сырой NRZI (как тест слота).""" + ch = str(channel).strip().upper() + if ch not in ("A", "B"): + raise ValueError("channel must be A or B") + try: + slot_n = int(slot) + except (TypeError, ValueError): + slot_n = 0 + slot_n = max(0, min(2249, slot_n)) + packet = build_slot_udp_payload(ch, slot_n, nrzi_body) + _send_udp(NRZI_UDP_HOST, NRZI_UDP_PORT, packet) + out: Dict[str, Any] = { + "dest": f"{NRZI_UDP_HOST}:{NRZI_UDP_PORT}", + "slot_channel": ch, + "slot": slot_n, + "payload_bytes": len(nrzi_body), + "udp_bytes": len(packet), + "errors": [], + } + if cfg is not None: + _append_gpio_result(out, tx_gpio_pulse(cfg, after_udp=True)) + return out + + +def send_transmission(cfg: Dict[str, Any], own: Dict[str, Any], which: str) -> Dict[str, Any]: + """ + Только NRZI по UDP на NRZI_UDP_HOST:NRZI_UDP_PORT. + which: '18', '19', '24A', '24B', 'broadcast' + """ + mode = cfg.get("nrzi_mode") or "packed" + ch = str(cfg.get("nrzi_slot_channel") or "A").strip().upper() + if ch not in ("A", "B"): + ch = "A" + try: + slot_n = int(cfg.get("nrzi_slot", 0)) + except (TypeError, ValueError): + slot_n = 0 + slot_n = max(0, min(2249, slot_n)) + + sent: Dict[str, Any] = { + "nrzi_bytes": 0, + "errors": [], + "dest": f"{NRZI_UDP_HOST}:{NRZI_UDP_PORT}", + "slot_channel": ch, + "slot": slot_n, + } + include_preamble = bool(cfg.get("include_nrzi_preamble", True)) + try: + pre_bits = int(cfg.get("nrzi_preamble_bits", 24)) + except (TypeError, ValueError): + pre_bits = 24 + pre_bits = max(0, min(128, pre_bits)) + pad_oct, pad_nrz = _nrzi_pad_opts(cfg) + + def one(data: Dict[str, Any], label: str) -> None: + try: + raw = _encode_nrzi_for_dict( + data, + cfg, + include_preamble=include_preamble, + mode=mode, + pre_bits=pre_bits, + pad_oct=pad_oct, + pad_nrz=pad_nrz, + ) + packet = build_slot_udp_payload(ch, slot_n, raw) + _send_udp(NRZI_UDP_HOST, NRZI_UDP_PORT, packet) + sent["nrzi_bytes"] += len(packet) + _append_gpio_result(sent, tx_gpio_pulse(cfg, after_udp=True)) + except Exception as e: + sent["errors"].append(f"NRZI {label}: {e}") + + if which == "broadcast": + one(build_type18_dict(cfg, own), "18") + one(build_type19_dict(cfg, own), "19") + one(build_type24a_dict(cfg), "24A") + one(build_type24b_dict(cfg), "24B") + elif which == "18": + one(build_type18_dict(cfg, own), "18") + elif which == "19": + one(build_type19_dict(cfg, own), "19") + elif which == "24A": + one(build_type24a_dict(cfg), "24A") + elif which == "24B": + one(build_type24b_dict(cfg), "24B") + else: + raise ValueError("which must be 18, 19, 24A, 24B, broadcast") + + return sent + + +def build_preview(cfg: Dict[str, Any], own: Dict[str, Any]) -> Dict[str, Any]: + """NRZI (hex) для типов 18, 19, 24A, 24B; отправка — на 127.0.0.1:6010.""" + if not MSG_CLASS: + raise RuntimeError("pyais не установлен") + mode = cfg.get("nrzi_mode") or "packed" + include_preamble = bool(cfg.get("include_nrzi_preamble", True)) + try: + pre_bits = int(cfg.get("nrzi_preamble_bits", 24)) + except (TypeError, ValueError): + pre_bits = 24 + pre_bits = max(0, min(128, pre_bits)) + pad_oct, pad_nrz = _nrzi_pad_opts(cfg) + + d18 = build_type18_dict(cfg, own) + d19 = build_type19_dict(cfg, own) + d24a = build_type24a_dict(cfg) + d24b = build_type24b_dict(cfg) + + ch = str(cfg.get("nrzi_slot_channel") or "A").strip().upper() + if ch not in ("A", "B"): + ch = "A" + try: + slot_n = int(cfg.get("nrzi_slot", 0)) + except (TypeError, ValueError): + slot_n = 0 + slot_n = max(0, min(2249, slot_n)) + + out: Dict[str, Any] = { + "nrzi_hex": {}, + "udp_frame_hex": {}, + "dest": f"{NRZI_UDP_HOST}:{NRZI_UDP_PORT}", + "slot_channel": ch, + "slot": slot_n, + "phy": { + "preamble_bits": pre_bits if include_preamble else 0, + "include_preamble": include_preamble, + "nrzi_mode": mode, + "nrzi_encoder": str(cfg.get("nrzi_encoder") or DEFAULT_TRANSPONDER["nrzi_encoder"]), + "aistx_phy_note": ( + "Кодер aistx: ais_nrzi_pipeline/phy.py (reverse bits, CRC как gr-aistx, кадр до 256 бит)." + if str(cfg.get("nrzi_encoder") or "").lower() == "aistx" + else None + ), + "fcs": "CRC-16-CCITT poly 0x1021, init 0xFFFF, XOROUT 0xFFFF; 16 бит FCS в потоке MSB первым", + "hdlc": "флаги 0x7E, bit stuffing между флагами", + "nrzi": "0 — переключение уровня, 1 — без изменения", + "packed": "8 NRZI-сэмплов на байт, первый бит — MSB байта (для UDP)", + "test_slot_note": ( + "«Тест слота» шлёт фиксированный TEST_AIS_NRZI (32 B, проверен на передатчике). " + "NRZI ниже — по умолчанию ais_nrzi_pipeline/phy.py (gr-aistx); при выборе ais_phy — другая CRC/битовый порядок." + ), + "pad_payload_to_octet": pad_oct, + "pad_nrz_total_bits": pad_nrz, + "per_message": {}, + }, + } + enc_ui = str(cfg.get("nrzi_encoder") or DEFAULT_TRANSPONDER["nrzi_encoder"]).strip().lower() + for key, d in [("18", d18), ("19", d19), ("24A", d24a), ("24B", d24b)]: + bits = payload_bits_from_dict(d, pad_payload_to_octet=pad_oct) + if enc_ui == "aistx" and _aistx_phy is not None: + plen = len(bits) + padb = (8 - (plen % 8)) % 8 + out["phy"]["per_message"][key] = { + "payload_bits_input": plen, + "payload_bits_after_pad": plen + padb, + "encoder": "aistx", + "hdlc_note": "статы HDLC/stuff — см. phy.py (gr-aistx)", + } + else: + out["phy"]["per_message"][key] = phy_frame_bit_counts( + bits, pad_payload_to_octet=pad_oct + ) + raw = _encode_nrzi_for_dict( + d, + cfg, + include_preamble=include_preamble, + mode=mode, + pre_bits=pre_bits, + pad_oct=pad_oct, + pad_nrz=pad_nrz, + ) + out["nrzi_hex"][key] = raw.hex() + out["udp_frame_hex"][key] = build_slot_udp_payload(ch, slot_n, raw).hex() + out["phy"]["per_message"][key]["nrzi_packed_bytes"] = len(raw) + out["phy"]["per_message"][key]["udp_total_bytes"] = 3 + len(raw) + return out