diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..9eab4e9 --- /dev/null +++ b/.env.example @@ -0,0 +1,10 @@ +POSTGRES_DB=postres +POSTGRES_USER=root +POSTGRES_PASSWORD=root + +DJANGO_SECRET_KEY=changeme +DJANGO_DEBUG=True +DB_HOST=localhost +DB_PORT=5432 + +VITE_API_URL=http://localhost:8000/api \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5dfa460 --- /dev/null +++ b/.gitignore @@ -0,0 +1,40 @@ +node_modules +dist +frontend/dist +frontend/dist/assets +frontend/dist/assets/index.html +frontend/dist/assets/index.html.gz +frontend/dist/assets/index.html.br +frontend/dist/assets/index.html.brotli +frontend/dist/assets/index.html.gzip +frontend/dist/assets/index.html.br +frontend/dist/assets/index.html.brotli +frontend/dist/assets/index.html.gzip +frontend/dist/assets/index.html.br +frontend/dist/assets/index.html.brotli +frontend/dist/assets/index.html.gzip +frontend/dist/assets/index.html.br +frontend/dist/assets/index.html.brotli +frontend/dist/assets/index.html.gzip +frontend/dist/assets/index.html.br +frontend/dist/assets/index.html.brotli +frontend/dist/assets/index.html.gzip +mobile/dist +backend/media +backend/staticfiles +backend/media +backend/staticfiles +.pycache__ +__pycache__ +*.pyc +*.pyo +*.pyd +*.db +*.sqlite3 +*.log +*.env +*.DS_Store +*.vscode +*.idea +*.swp +*.swo \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..926efca --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,105 @@ +# AGENTS.md — Cloud Reader monorepo + +Guidance for AI agents and contributors working in this repository. + +## Repository layout + +``` +cloud-reader/ +├── backend/ # Django REST API (canonical backend) +├── frontend/ # React + Vite + TypeScript (web) +├── mobile/ # Expo React Native app +├── packages/shared/ # @cloud-reader/shared types & utils +├── docs/ # Feature specs and architecture notes +└── docker-compose.yml +``` + +- **Backend** is the source of truth for API contracts, auth, and persistence. +- **Frontend** and **mobile** consume the same REST API; share domain types via `@cloud-reader/shared` where practical. +- Do not reintroduce removed `api/` or `web/` directories. + +## Documentation conventions + +| Location | Purpose | +|----------|---------| +| `docs/NNN-*.md` | Cross-cutting or product specs (e.g. `001-customizable-mobile-reading-experience.md`) | +| `docs/backend/NNN-*.md` | Backend feature specs; use next sequential number (currently `010`) | +| `docs/mobile/NNN-*.md` | Mobile (Expo) feature specs; use next sequential number (currently `010`) | +| `docs/frontend/*.md` | Frontend-specific specs | + +When adding a **major backend feature**: + +1. Implement in `backend/apps//`. +2. Add or update a numbered spec under `docs/backend/`. +3. Include: objective, API contracts, models/services touched, env vars, and verification steps. + +Do not edit plan files in `.cursor/plans/` unless explicitly asked. + +## Backend (Django) + +- **Python:** 3.12+, managed with `uv` (`backend/pyproject.toml`, `backend/uv.lock`). +- **Settings:** `backend/config/settings.py` (pydantic-settings) + `backend/config/django.py`. +- **Apps:** `users`, `books`, `annotations`, `reader`. +- **Auth:** JWT via `djangorestframework-simplejwt`; register/login at `/api/auth/`. +- **Default permission:** `IsAuthenticated` — public endpoints must set `AllowAny` explicitly. +- **URL routing:** Single `DefaultRouter` in `books/urls.py`; register specific prefixes (e.g. `ebooks`) **before** the empty `""` book route to avoid `{pk}` shadowing. +- **Services:** Put non-trivial logic in `apps//services/` (not in views/serializers). +- **Migrations:** Run after model changes; prefer reusing existing JSON fields (e.g. `EBook.metadata_json`) before new columns. +- **Tests:** Only add when requested or when they cover non-obvious behavior. + +### Books domain + +- **`Book`:** Catalog/discovery entity (genres, reading status). +- **`EBook`:** Per-user uploaded file (EPUB/PDF); primary import path via `POST /api/books/ebooks/`. +- **`metadata_json` on EBook:** External metadata (Open Library, etc.). +- **`cover_image` on EBook:** Stored file; library UI reads it from list/detail serializers. + +## Frontend (web) + +- **Entry:** `frontend/src/main.tsx` mounts `App.tsx` (React Router + auth). +- **API client:** `frontend/src/api/client.ts` (axios, JWT refresh, base URL `/api`). +- **Auth routes:** `/auth` (not `/login`). +- **Imports:** Upload via `booksApi.uploadEBook`; library lists user `EBook`s. +- **Styling:** Mix of inline styles and CSS modules; match surrounding patterns. +- Do not add tests or new `.md` files unless requested. + +## Mobile (Expo) + +- Uses `@cloud-reader/shared` and mirrors web API patterns. +- Token storage: AsyncStorage; base URL from `EXPO_PUBLIC_API_URL`. + +## Shared package + +- `packages/shared/src/types.ts` — domain types for web/mobile. +- `packages/shared/src/utils.ts` — API endpoint constants, helpers. +- Keep camelCase in TS; backend JSON may use snake_case (DRF default). + +## Code change principles + +1. **Minimize scope** — smallest correct diff; no drive-by refactors. +2. **Match conventions** — read neighboring code before adding new patterns. +3. **No over-engineering** — no extra abstractions for one-off use. +4. **Comments** — only for non-obvious business logic. +5. **Imports** — remove unused imports; delete dead code after refactors. +6. **Secrets** — never commit `.env`; document vars in `.env.example` only. + +## Git & PRs + +- Commit only when the user asks. +- Do not force-push `main`/`master`. +- Use `gh` for GitHub PRs when requested. + +## Postman + +- Use camelCase operation names (`listSomething`, `createSomething`). +- Ask which env vars the user uses; output JSON to copy, not a new file. + +## Commands (run locally when needed) + +```bash +# Backend +cd backend && uv sync && uv run python manage.py migrate && uv run python manage.py runserver + +# Frontend +cd frontend && yarn install && yarn dev +``` diff --git a/README.md b/README.md index 16cbd76..c55ab13 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,9 @@ cloud-reader/ ### Docker (recommended) ```bash +cp env.example .env +``` +```bash docker compose up --build ``` - **Frontend:** http://localhost:5173 diff --git a/backend/.env.example b/backend/.env.example index 2e7190f..c1d2add 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -5,4 +5,12 @@ DB_NAME=cloud_reader DB_USER=postgres DB_PASSWORD=postgres DB_HOST=localhost -DB_PORT=5432 \ No newline at end of file +DB_PORT=5432 + +# Open Library metadata enrichment (optional) +OPENLIBRARY_ENABLED=true +OPENLIBRARY_PREFERRED_LANG=es +OPENLIBRARY_FALLBACK_LANG=en +OPENLIBRARY_TIMEOUT_SECONDS=10 +OPENLIBRARY_CONNECT_TIMEOUT_SECONDS=15 +OPENLIBRARY_USER_AGENT=CloudReader/1.0 \ No newline at end of file diff --git a/backend/.python-version b/backend/.python-version new file mode 100644 index 0000000..e4fba21 --- /dev/null +++ b/backend/.python-version @@ -0,0 +1 @@ +3.12 diff --git a/backend/apps/annotations/admin.py b/backend/apps/annotations/admin.py index 6b5dac9..95e2a75 100644 --- a/backend/apps/annotations/admin.py +++ b/backend/apps/annotations/admin.py @@ -5,9 +5,17 @@ from apps.annotations.models import Bookmark, Note @admin.register(Bookmark) class BookmarkAdmin(admin.ModelAdmin): - list_display = ("user", "book", "page", "created_at") - list_select_related = ("user", "book") - search_fields = ("user__email", "book__title", "location_text") + list_display = ( + "user", + "ebook", + "chapter_index", + "chapter_title", + "page", + "highlight_color", + "created_at", + ) + list_select_related = ("user", "ebook") + search_fields = ("user__email", "ebook__title", "location_text", "content") list_filter = ("created_at",) diff --git a/backend/apps/annotations/migrations/0001_initial.py b/backend/apps/annotations/migrations/0001_initial.py new file mode 100644 index 0000000..14e7730 --- /dev/null +++ b/backend/apps/annotations/migrations/0001_initial.py @@ -0,0 +1,57 @@ +# Generated by Django 5.1.7 on 2026-06-03 22:27 + +import django.db.models.deletion +import uuid +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('books', '0002_ebook_file_size_ebook_format_ebook_metadata_json_and_more'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Note', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('page', models.PositiveIntegerField()), + ('location_text', models.TextField(blank=True, default='', help_text='The selected passage text this note refers to')), + ('content', models.TextField(help_text='The note body content')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('book', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='notes', to='books.book')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='notes', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'verbose_name': 'Note', + 'verbose_name_plural': 'Notes', + 'db_table': 'annotations_note', + 'ordering': ['-created_at'], + }, + ), + migrations.CreateModel( + name='Bookmark', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('page', models.PositiveIntegerField()), + ('location_text', models.TextField(blank=True, default='', help_text='The selected passage text at this location')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('book', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='bookmarks', to='books.book')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='bookmarks', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'verbose_name': 'Bookmark', + 'verbose_name_plural': 'Bookmarks', + 'db_table': 'annotations_bookmark', + 'ordering': ['-created_at'], + 'constraints': [models.UniqueConstraint(fields=('user', 'book', 'page'), name='uq_bookmark_user_book_page')], + }, + ), + ] diff --git a/backend/apps/annotations/migrations/0002_bookmark_ebook_epub_fields.py b/backend/apps/annotations/migrations/0002_bookmark_ebook_epub_fields.py new file mode 100644 index 0000000..25ba5d2 --- /dev/null +++ b/backend/apps/annotations/migrations/0002_bookmark_ebook_epub_fields.py @@ -0,0 +1,93 @@ +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +def clear_legacy_bookmarks(apps, schema_editor): + Bookmark = apps.get_model("annotations", "Bookmark") + Bookmark.objects.all().delete() + + +class Migration(migrations.Migration): + + dependencies = [ + ("books", "0003_readingprogress_epub_location"), + ("annotations", "0001_initial"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.RunPython(clear_legacy_bookmarks, migrations.RunPython.noop), + migrations.RemoveConstraint( + model_name="bookmark", + name="uq_bookmark_user_book_page", + ), + migrations.RemoveField( + model_name="bookmark", + name="book", + ), + migrations.AddField( + model_name="bookmark", + name="ebook", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="bookmarks", + to="books.ebook", + null=True, + ), + ), + migrations.AddField( + model_name="bookmark", + name="epub_cfi", + field=models.CharField(db_index=True, default="", max_length=2048), + preserve_default=False, + ), + migrations.AddField( + model_name="bookmark", + name="chapter_index", + field=models.PositiveIntegerField(db_index=True, default=0), + ), + migrations.AddField( + model_name="bookmark", + name="chapter_title", + field=models.CharField(blank=True, default="", max_length=512), + ), + migrations.AddField( + model_name="bookmark", + name="content", + field=models.TextField(blank=True, default=""), + ), + migrations.AlterField( + model_name="bookmark", + name="page", + field=models.PositiveIntegerField( + default=1, + help_text="Legacy/display page; derived from chapter_index + 1", + ), + ), + migrations.AlterModelOptions( + name="bookmark", + options={ + "ordering": ["chapter_index", "epub_cfi"], + "verbose_name": "Bookmark", + "verbose_name_plural": "Bookmarks", + }, + ), + # ebook was added nullable for SQLite; enforce NOT NULL via AlterField + migrations.AlterField( + model_name="bookmark", + name="ebook", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="bookmarks", + to="books.ebook", + ), + ), + migrations.AddConstraint( + model_name="bookmark", + constraint=models.UniqueConstraint( + fields=("user", "ebook", "epub_cfi"), + name="uq_bookmark_user_ebook_cfi", + ), + ), + ] diff --git a/backend/apps/annotations/migrations/0003_bookmark_highlight_color.py b/backend/apps/annotations/migrations/0003_bookmark_highlight_color.py new file mode 100644 index 0000000..16a81e6 --- /dev/null +++ b/backend/apps/annotations/migrations/0003_bookmark_highlight_color.py @@ -0,0 +1,16 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("annotations", "0002_bookmark_ebook_epub_fields"), + ] + + operations = [ + migrations.AddField( + model_name="bookmark", + name="highlight_color", + field=models.CharField(default="#fde047", max_length=7), + ), + ] diff --git a/backend/apps/annotations/migrations/0004_alter_bookmark_content_and_more.py b/backend/apps/annotations/migrations/0004_alter_bookmark_content_and_more.py new file mode 100644 index 0000000..6acf011 --- /dev/null +++ b/backend/apps/annotations/migrations/0004_alter_bookmark_content_and_more.py @@ -0,0 +1,23 @@ +# Generated by Django 5.1.7 on 2026-06-04 03:31 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('annotations', '0003_bookmark_highlight_color'), + ] + + operations = [ + migrations.AlterField( + model_name='bookmark', + name='content', + field=models.TextField(blank=True, default='', help_text='Optional user thought; empty means bookmark-only'), + ), + migrations.AlterField( + model_name='bookmark', + name='highlight_color', + field=models.CharField(default='#fde047', help_text='Hex color for in-book passage highlight (e.g. #fde047)', max_length=7), + ), + ] diff --git a/backend/apps/annotations/migrations/__init__.py b/backend/apps/annotations/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/apps/annotations/models.py b/backend/apps/annotations/models.py index fff70d1..4039224 100644 --- a/backend/apps/annotations/models.py +++ b/backend/apps/annotations/models.py @@ -5,7 +5,7 @@ from django.db import models class Bookmark(models.Model): - """A saved location in a book that the user can return to.""" + """A saved passage anchor in an uploaded ebook (EPUB CFI + optional thought).""" id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) user = models.ForeignKey( @@ -14,18 +14,34 @@ class Bookmark(models.Model): related_name="bookmarks", db_index=True, ) - book = models.ForeignKey( - "books.Book", + ebook = models.ForeignKey( + "books.EBook", on_delete=models.CASCADE, related_name="bookmarks", db_index=True, ) - page = models.PositiveIntegerField() + epub_cfi = models.CharField(max_length=2048, db_index=True) + chapter_index = models.PositiveIntegerField(default=0, db_index=True) + chapter_title = models.CharField(max_length=512, blank=True, default="") + page = models.PositiveIntegerField( + default=1, + help_text="Legacy/display page; derived from chapter_index + 1", + ) location_text = models.TextField( blank=True, default="", help_text="The selected passage text at this location", ) + content = models.TextField( + blank=True, + default="", + help_text="Optional user thought; empty means bookmark-only", + ) + highlight_color = models.CharField( + max_length=7, + default="#fde047", + help_text="Hex color for in-book passage highlight (e.g. #fde047)", + ) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) @@ -33,20 +49,20 @@ class Bookmark(models.Model): db_table = "annotations_bookmark" verbose_name = "Bookmark" verbose_name_plural = "Bookmarks" - ordering = ["-created_at"] + ordering = ["chapter_index", "epub_cfi"] constraints = [ models.UniqueConstraint( - fields=["user", "book", "page"], - name="uq_bookmark_user_book_page", + fields=["user", "ebook", "epub_cfi"], + name="uq_bookmark_user_ebook_cfi", ) ] def __str__(self) -> str: - return f"{self.user} @ {self.book} p.{self.page}" + return f"{self.user} @ {self.ebook} ch.{self.chapter_index}" class Note(models.Model): - """A user-written note attached to a specific location in a book.""" + """Legacy note model; new UX uses Bookmark.content instead.""" id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) user = models.ForeignKey( @@ -81,4 +97,4 @@ class Note(models.Model): def __str__(self) -> str: preview = self.content[:50] - return f"{self.user} @ {self.book} p.{self.page}: {preview}" \ No newline at end of file + return f"{self.user} @ {self.book} p.{self.page}: {preview}" diff --git a/backend/apps/annotations/serializers.py b/backend/apps/annotations/serializers.py index 6b3981b..336a57e 100644 --- a/backend/apps/annotations/serializers.py +++ b/backend/apps/annotations/serializers.py @@ -1,62 +1,86 @@ +import re + from rest_framework import serializers from apps.annotations.models import Bookmark, Note +_HEX_COLOR_RE = re.compile(r"^#[0-9A-Fa-f]{6}$") + + +def validate_highlight_color(value: str) -> str: + stripped = (value or "").strip() + if not _HEX_COLOR_RE.match(stripped): + raise serializers.ValidationError("highlight_color must be a hex color like #fde047.") + return stripped.lower() + class BookmarkSerializer(serializers.ModelSerializer): - """Serialize Bookmark data with full details.""" - - book_title = serializers.CharField(source="book.title", read_only=True) + ebook_title = serializers.CharField(source="ebook.title", read_only=True) class Meta: model = Bookmark fields = [ "id", - "book", - "book_title", + "ebook", + "ebook_title", + "epub_cfi", + "chapter_index", + "chapter_title", "page", "location_text", + "content", + "highlight_color", "created_at", "updated_at", ] - read_only_fields = ["id", "created_at", "updated_at", "book_title"] - - def validate_page(self, value: int) -> int: - if value < 1: - raise serializers.ValidationError("Page must be a positive integer.") - return value + read_only_fields = ["id", "created_at", "updated_at", "ebook_title", "page"] class BookmarkCreateSerializer(serializers.ModelSerializer): - """Serializer used for creating bookmarks. Sets user from request context.""" - class Meta: model = Bookmark - fields = ["book", "page", "location_text"] + fields = [ + "ebook", + "epub_cfi", + "chapter_index", + "chapter_title", + "location_text", + "content", + "highlight_color", + ] - def validate_page(self, value: int) -> int: - if value < 1: - raise serializers.ValidationError("Page must be a positive integer.") + def validate_highlight_color(self, value: str) -> str: + return validate_highlight_color(value) + + def validate_epub_cfi(self, value: str) -> str: + stripped = (value or "").strip() + if not stripped: + raise serializers.ValidationError("epub_cfi is required.") + return stripped + + def validate_chapter_index(self, value: int) -> int: + if value < 0: + raise serializers.ValidationError("chapter_index must be non-negative.") return value def validate(self, attrs): user = self.context["request"].user - if Bookmark.objects.filter( - user=user, book=attrs["book"], page=attrs["page"] - ).exists(): + ebook = attrs["ebook"] + epub_cfi = attrs["epub_cfi"] + if Bookmark.objects.filter(user=user, ebook=ebook, epub_cfi=epub_cfi).exists(): raise serializers.ValidationError( - {"page": "A bookmark already exists at this page for this book."} + {"epub_cfi": "A marker already exists for this passage."} ) return attrs def create(self, validated_data): validated_data["user"] = self.context["request"].user + validated_data["page"] = validated_data.get("chapter_index", 0) + 1 + validated_data["content"] = (validated_data.get("content") or "").strip() return super().create(validated_data) class NoteSerializer(serializers.ModelSerializer): - """Serialize Note data with full details.""" - book_title = serializers.CharField(source="book.title", read_only=True) class Meta: @@ -78,16 +102,8 @@ class NoteSerializer(serializers.ModelSerializer): raise serializers.ValidationError("Page must be a positive integer.") return value - def validate_content(self, value: str) -> str: - stripped = value.strip() - if not stripped: - raise serializers.ValidationError("Note content cannot be empty.") - return stripped - class NoteCreateSerializer(serializers.ModelSerializer): - """Serializer used for creating notes. Sets user from request context.""" - class Meta: model = Note fields = ["book", "page", "location_text", "content"] @@ -105,4 +121,4 @@ class NoteCreateSerializer(serializers.ModelSerializer): def create(self, validated_data): validated_data["user"] = self.context["request"].user - return super().create(validated_data) \ No newline at end of file + return super().create(validated_data) diff --git a/backend/apps/annotations/tests.py b/backend/apps/annotations/tests.py index 86ef6ba..d65aff8 100644 --- a/backend/apps/annotations/tests.py +++ b/backend/apps/annotations/tests.py @@ -6,14 +6,10 @@ from rest_framework import status from rest_framework.test import APIClient from apps.annotations.models import Bookmark, Note -from apps.books.models import Book +from apps.books.models import Book, EBook from apps.users.models import User -# --------------------------------------------------------------------------- -# Fixtures -# --------------------------------------------------------------------------- - @pytest.fixture def api_client() -> APIClient: return APIClient() @@ -53,12 +49,26 @@ def book() -> Book: @pytest.fixture -def bookmark(auth_client, user: User, book: Book) -> Bookmark: +def ebook(user: User) -> EBook: + return EBook.objects.create( + user=user, + title="Test Ebook", + author="Test Author", + format="epub", + ) + + +@pytest.fixture +def bookmark(auth_client, user: User, ebook: EBook) -> Bookmark: return Bookmark.objects.create( user=user, - book=book, - page=42, + ebook=ebook, + epub_cfi="epubcfi(/6/4!/4/2,/1:0,/1:10)", + chapter_index=2, + chapter_title="Chapter 3", + page=3, location_text="important passage", + content="", ) @@ -73,259 +83,84 @@ def note(auth_client, user: User, book: Book) -> Note: ) -# --------------------------------------------------------------------------- -# Bookmark tests -# --------------------------------------------------------------------------- - class TestBookmarkList: url = reverse("bookmark-list") - def test_unauthenticated_user_cannot_list(self, api_client: APIClient): + def test_list_requires_auth(self, api_client: APIClient): response = api_client.get(self.url) assert response.status_code == status.HTTP_401_UNAUTHORIZED def test_list_returns_user_bookmarks_only( - self, auth_client: APIClient, user: User, other_user: User, book: Book + self, auth_client: APIClient, user: User, other_user: User, ebook: EBook ): - Bookmark.objects.create(user=user, book=book, page=1) - Bookmark.objects.create(user=other_user, book=book, page=2) - + Bookmark.objects.create( + user=user, + ebook=ebook, + epub_cfi="epubcfi(/6/4!/4/2,/1:0,/1:5)", + chapter_index=0, + page=1, + ) + other_ebook = EBook.objects.create(user=other_user, title="Other", format="epub") + Bookmark.objects.create( + user=other_user, + ebook=other_ebook, + epub_cfi="epubcfi(/6/4!/4/2,/2:0,/2:5)", + chapter_index=0, + page=1, + ) response = auth_client.get(self.url) assert response.status_code == status.HTTP_200_OK results = response.data["results"] assert len(results) == 1 - assert results[0]["page"] == 1 - def test_list_returns_empty_when_no_bookmarks( - self, auth_client: APIClient - ): - response = auth_client.get(self.url) + def test_filter_by_ebook(self, auth_client: APIClient, bookmark: Bookmark, ebook: EBook): + response = auth_client.get(self.url, {"ebook": str(ebook.id)}) assert response.status_code == status.HTTP_200_OK - assert response.data["count"] == 0 - - def test_list_orders_by_newest_first( - self, auth_client: APIClient, user: User, book: Book - ): - b1 = Bookmark.objects.create(user=user, book=book, page=1) - b2 = Bookmark.objects.create(user=user, book=book, page=2) - response = auth_client.get(self.url) - results = response.data["results"] - assert results[0]["page"] == 2 - assert results[1]["page"] == 1 - - def test_list_includes_book_title( - self, auth_client: APIClient, bookmark: Bookmark - ): - response = auth_client.get(self.url) - assert response.status_code == status.HTTP_200_OK - assert response.data["results"][0]["book_title"] == "Test Book" + assert len(response.data["results"]) == 1 class TestBookmarkCreate: url = reverse("bookmark-list") - def test_create_bookmark(self, auth_client: APIClient, book: Book): - data = {"book": str(book.id), "page": 10, "location_text": "key insight"} + def test_create_marker(self, auth_client: APIClient, ebook: EBook): + data = { + "ebook": ebook.id, + "epub_cfi": "epubcfi(/6/4!/4/2,/1:0,/1:20)", + "chapter_index": 1, + "chapter_title": "Chapter 2", + "location_text": "Selected text", + "content": "My thought", + } response = auth_client.post(self.url, data, format="json") assert response.status_code == status.HTTP_201_CREATED - assert response.data["page"] == 10 + assert response.data["content"] == "My thought" + assert response.data["ebook"] == ebook.id - def test_create_bookmark_without_location_text( - self, auth_client: APIClient, book: Book - ): - data = {"book": str(book.id), "page": 5} + def test_create_bookmark_only_empty_content(self, auth_client: APIClient, ebook: EBook): + data = { + "ebook": ebook.id, + "epub_cfi": "epubcfi(/6/4!/4/2,/3:0,/3:8)", + "chapter_index": 0, + "location_text": "Quote only", + "content": "", + } response = auth_client.post(self.url, data, format="json") assert response.status_code == status.HTTP_201_CREATED - assert response.data["page"] == 5 + assert response.data["content"] == "" - def test_duplicate_bookmark_page_is_rejected( - self, auth_client: APIClient, bookmark: Bookmark - ): - data = {"book": str(bookmark.book.id), "page": bookmark.page} - response = auth_client.post(self.url, data, format="json") - assert response.status_code == status.HTTP_400_BAD_REQUEST - - def test_unauthenticated_user_cannot_create( - self, api_client: APIClient, book: Book - ): - data = {"book": str(book.id), "page": 10} - response = api_client.post(self.url, data, format="json") - assert response.status_code == status.HTTP_401_UNAUTHORIZED - - def test_invalid_page_rejected( - self, auth_client: APIClient, book: Book - ): - data = {"book": str(book.id), "page": 0} + def test_duplicate_cfi_rejected(self, auth_client: APIClient, bookmark: Bookmark, ebook: EBook): + data = { + "ebook": ebook.id, + "epub_cfi": bookmark.epub_cfi, + "chapter_index": 0, + "location_text": "dup", + } response = auth_client.post(self.url, data, format="json") assert response.status_code == status.HTTP_400_BAD_REQUEST class TestBookmarkDetail: - def test_get_bookmark( - self, auth_client: APIClient, bookmark: Bookmark - ): - url = reverse("bookmark-detail", args=[str(bookmark.id)]) - response = auth_client.get(url) - assert response.status_code == status.HTTP_200_OK - assert response.data["page"] == bookmark.page - - def test_cannot_access_other_users_bookmark( - self, api_client: APIClient, other_user: User, bookmark: Bookmark - ): - api_client.force_authenticate(user=other_user) - url = reverse("bookmark-detail", args=[str(bookmark.id)]) - response = api_client.get(url) - assert response.status_code == status.HTTP_404_NOT_FOUND - - -class TestBookmarkDelete: - def test_delete_bookmark( - self, auth_client: APIClient, bookmark: Bookmark - ): + def test_delete_bookmark(self, auth_client: APIClient, bookmark: Bookmark): url = reverse("bookmark-detail", args=[str(bookmark.id)]) response = auth_client.delete(url) assert response.status_code == status.HTTP_204_NO_CONTENT - assert Bookmark.objects.count() == 0 - - def test_cannot_delete_other_users_bookmark( - self, api_client: APIClient, other_user: User, bookmark: Bookmark - ): - api_client.force_authenticate(user=other_user) - url = reverse("bookmark-detail", args=[str(bookmark.id)]) - response = api_client.delete(url) - assert response.status_code == status.HTTP_404_NOT_FOUND - - -class TestBookmarkFilterByBook: - def test_filter_by_book( - self, auth_client: APIClient, user: User, book: Book - ): - other_book = Book.objects.create(title="Other", author="Other") - Bookmark.objects.create(user=user, book=book, page=1) - Bookmark.objects.create(user=user, book=other_book, page=2) - - url = reverse("bookmark-list") - response = auth_client.get(url, {"book": str(book.id)}) - assert response.status_code == status.HTTP_200_OK - assert response.data["count"] == 1 - assert response.data["results"][0]["page"] == 1 - - -# --------------------------------------------------------------------------- -# Note tests -# --------------------------------------------------------------------------- - -class TestNoteList: - url = reverse("note-list") - - def test_unauthenticated_user_cannot_list(self, api_client: APIClient): - response = api_client.get(self.url) - assert response.status_code == status.HTTP_401_UNAUTHORIZED - - def test_list_returns_user_notes_only( - self, auth_client: APIClient, user: User, other_user: User, book: Book - ): - Note.objects.create(user=user, book=book, page=1, content="My note") - Note.objects.create(user=other_user, book=book, page=2, content="Other's note") - - response = auth_client.get(self.url) - assert response.status_code == status.HTTP_200_OK - results = response.data["results"] - assert len(results) == 1 - assert results[0]["content"] == "My note" - - def test_list_includes_book_title( - self, auth_client: APIClient, note: Note - ): - response = auth_client.get(self.url) - assert response.status_code == status.HTTP_200_OK - assert response.data["results"][0]["book_title"] == "Test Book" - - -class TestNoteCreate: - url = reverse("note-list") - - def test_create_note(self, auth_client: APIClient, book: Book): - data = { - "book": str(book.id), - "page": 20, - "location_text": "interesting part", - "content": "This is a thoughtful note.", - } - response = auth_client.post(self.url, data, format="json") - assert response.status_code == status.HTTP_201_CREATED - assert response.data["content"] == "This is a thoughtful note." - - def test_create_note_without_location_text( - self, auth_client: APIClient, book: Book - ): - data = {"book": str(book.id), "page": 20, "content": "A note."} - response = auth_client.post(self.url, data, format="json") - assert response.status_code == status.HTTP_201_CREATED - - def test_empty_content_rejected( - self, auth_client: APIClient, book: Book - ): - data = {"book": str(book.id), "page": 20, "content": " "} - response = auth_client.post(self.url, data, format="json") - assert response.status_code == status.HTTP_400_BAD_REQUEST - - def test_unauthenticated_user_cannot_create( - self, api_client: APIClient, book: Book - ): - data = {"book": str(book.id), "page": 20, "content": "Note"} - response = api_client.post(self.url, data, format="json") - assert response.status_code == status.HTTP_401_UNAUTHORIZED - - -class TestNoteUpdate: - def test_update_note_content( - self, auth_client: APIClient, note: Note - ): - url = reverse("note-detail", args=[str(note.id)]) - data = {"content": "Updated note content."} - response = auth_client.patch(url, data, format="json") - assert response.status_code == status.HTTP_200_OK - assert response.data["content"] == "Updated note content." - - def test_cannot_update_other_users_note( - self, api_client: APIClient, other_user: User, note: Note - ): - api_client.force_authenticate(user=other_user) - url = reverse("note-detail", args=[str(note.id)]) - data = {"content": "Hacked!"} - response = api_client.patch(url, data, format="json") - assert response.status_code == status.HTTP_404_NOT_FOUND - - -class TestNoteDelete: - def test_delete_note(self, auth_client: APIClient, note: Note): - url = reverse("note-detail", args=[str(note.id)]) - response = auth_client.delete(url) - assert response.status_code == status.HTTP_204_NO_CONTENT - assert Note.objects.count() == 0 - - def test_batch_delete_notes( - self, auth_client: APIClient, user: User, book: Book - ): - n1 = Note.objects.create(user=user, book=book, page=1, content="A") - n2 = Note.objects.create(user=user, book=book, page=2, content="B") - url = reverse("note-batch-delete") - response = auth_client.delete(url, {"ids": [str(n1.id), str(n2.id)]}, format="json") - assert response.status_code == status.HTTP_200_OK - assert response.data["deleted"] == 2 - - -class TestNoteFilterByBook: - def test_filter_by_book( - self, auth_client: APIClient, user: User, book: Book - ): - other_book = Book.objects.create(title="Other", author="Other") - Note.objects.create(user=user, book=book, page=1, content="In book") - Note.objects.create(user=user, book=other_book, page=2, content="In other") - - url = reverse("note-list") - response = auth_client.get(url, {"book": str(book.id)}) - assert response.status_code == status.HTTP_200_OK - assert response.data["count"] == 1 - assert response.data["results"][0]["content"] == "In book" \ No newline at end of file diff --git a/backend/apps/annotations/views.py b/backend/apps/annotations/views.py index 0ddafe4..6f59638 100644 --- a/backend/apps/annotations/views.py +++ b/backend/apps/annotations/views.py @@ -16,14 +16,14 @@ from apps.annotations.serializers import ( class BookmarkViewSet(viewsets.ModelViewSet): - """CRUD for user bookmarks. Users can only manage their own bookmarks.""" + """CRUD for user ebook markers (passage anchors + optional thoughts).""" permission_classes = [IsAuthenticated, IsOwner] filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter] - filterset_fields = ["book"] - search_fields = ["location_text"] - ordering_fields = ["created_at", "page"] - ordering = ["-created_at"] + filterset_fields = ["ebook"] + search_fields = ["location_text", "content", "chapter_title"] + ordering_fields = ["chapter_index", "epub_cfi", "created_at"] + ordering = ["chapter_index", "epub_cfi"] def get_serializer_class(self): if self.action == "create": @@ -31,16 +31,13 @@ class BookmarkViewSet(viewsets.ModelViewSet): return BookmarkSerializer def get_queryset(self): - return Bookmark.objects.filter(user=self.request.user).select_related( - "book" - ) + return Bookmark.objects.filter(user=self.request.user).select_related("ebook") def perform_create(self, serializer): serializer.save(user=self.request.user) @action(detail=False, methods=["delete"], url_path="batch-delete") def batch_delete(self, request): - """Delete multiple bookmarks by id list.""" ids = request.data.get("ids", []) if not ids: return Response( @@ -49,13 +46,11 @@ class BookmarkViewSet(viewsets.ModelViewSet): deleted, _ = Bookmark.objects.filter( id__in=ids, user=request.user ).delete() - return Response( - {"deleted": deleted}, status=status.HTTP_200_OK - ) + return Response({"deleted": deleted}, status=status.HTTP_200_OK) class NoteViewSet(viewsets.ModelViewSet): - """CRUD for user notes. Users can only manage their own notes.""" + """Legacy notes API (catalog Book FK).""" permission_classes = [IsAuthenticated, IsOwner] filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter] @@ -70,16 +65,13 @@ class NoteViewSet(viewsets.ModelViewSet): return NoteSerializer def get_queryset(self): - return Note.objects.filter(user=self.request.user).select_related( - "book" - ) + return Note.objects.filter(user=self.request.user).select_related("book") def perform_create(self, serializer): serializer.save(user=self.request.user) @action(detail=False, methods=["delete"], url_path="batch-delete") def batch_delete(self, request): - """Delete multiple notes by id list.""" ids = request.data.get("ids", []) if not ids: return Response( @@ -88,6 +80,4 @@ class NoteViewSet(viewsets.ModelViewSet): deleted, _ = Note.objects.filter( id__in=ids, user=request.user ).delete() - return Response( - {"deleted": deleted}, status=status.HTTP_200_OK - ) \ No newline at end of file + return Response({"deleted": deleted}, status=status.HTTP_200_OK) diff --git a/backend/apps/books/migrations/0002_ebook_file_size_ebook_format_ebook_metadata_json_and_more.py b/backend/apps/books/migrations/0002_ebook_file_size_ebook_format_ebook_metadata_json_and_more.py new file mode 100644 index 0000000..07e4a84 --- /dev/null +++ b/backend/apps/books/migrations/0002_ebook_file_size_ebook_format_ebook_metadata_json_and_more.py @@ -0,0 +1,105 @@ +# Generated by Django 5.1.7 on 2026-06-03 22:21 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('books', '0001_initial'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AddField( + model_name='ebook', + name='file_size', + field=models.BigIntegerField(default=0), + ), + migrations.AddField( + model_name='ebook', + name='format', + field=models.CharField(blank=True, default='', editable=False, max_length=20), + ), + migrations.AddField( + model_name='ebook', + name='metadata_json', + field=models.JSONField(blank=True, default=dict), + ), + migrations.AddField( + model_name='ebook', + name='page_count', + field=models.PositiveIntegerField(default=0), + ), + migrations.AddField( + model_name='readingprogress', + name='device_id', + field=models.CharField(blank=True, default='', max_length=128), + ), + migrations.AddField( + model_name='readingprogress', + name='device_name', + field=models.CharField(blank=True, default='', max_length=128), + ), + migrations.AddField( + model_name='readingprogress', + name='version', + field=models.PositiveIntegerField(default=1), + ), + migrations.AlterField( + model_name='ebook', + name='author', + field=models.CharField(blank=True, db_index=True, default='', max_length=256), + ), + migrations.AlterField( + model_name='ebook', + name='title', + field=models.CharField(db_index=True, max_length=512), + ), + migrations.CreateModel( + name='BookChapter', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=512)), + ('index', models.IntegerField(default=0)), + ('href', models.CharField(blank=True, default='', max_length=1024)), + ('children', models.JSONField(blank=True, default=list)), + ('ebook', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='chapters', to='books.ebook')), + ], + options={ + 'verbose_name': 'Book Chapter', + 'verbose_name_plural': 'Book Chapters', + 'db_table': 'books_book_chapter', + 'ordering': ['index'], + }, + ), + migrations.CreateModel( + name='DownloadRecord', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('file_size', models.BigIntegerField(default=0)), + ('downloaded_at', models.DateTimeField(auto_now_add=True)), + ('ebook', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='download_records', to='books.ebook')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='download_records', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'verbose_name': 'Download Record', + 'verbose_name_plural': 'Download Records', + 'db_table': 'books_download_record', + 'ordering': ['-downloaded_at'], + }, + ), + migrations.DeleteModel( + name='ReadingSettings', + ), + migrations.AddIndex( + model_name='bookchapter', + index=models.Index(fields=['ebook', 'index'], name='books_book__ebook_i_464cd6_idx'), + ), + migrations.AlterUniqueTogether( + name='downloadrecord', + unique_together={('user', 'ebook')}, + ), + ] diff --git a/backend/apps/books/migrations/0003_readingprogress_epub_location.py b/backend/apps/books/migrations/0003_readingprogress_epub_location.py new file mode 100644 index 0000000..1755483 --- /dev/null +++ b/backend/apps/books/migrations/0003_readingprogress_epub_location.py @@ -0,0 +1,16 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("books", "0002_ebook_file_size_ebook_format_ebook_metadata_json_and_more"), + ] + + operations = [ + migrations.AddField( + model_name="readingprogress", + name="epub_location", + field=models.CharField(blank=True, default="", max_length=2048), + ), + ] diff --git a/backend/apps/books/models.py b/backend/apps/books/models.py index bc16919..ba93827 100644 --- a/backend/apps/books/models.py +++ b/backend/apps/books/models.py @@ -111,6 +111,7 @@ class ReadingProgress(models.Model): ebook = models.OneToOneField(EBook, on_delete=models.CASCADE, related_name="reading_progress") current_position = models.FloatField(default=0.0) last_page = models.IntegerField(default=0) + epub_location = models.CharField(max_length=2048, blank=True, default="") device_id = models.CharField(max_length=128, blank=True, default="") device_name = models.CharField(max_length=128, blank=True, default="") version = models.PositiveIntegerField(default=1) diff --git a/backend/apps/books/serializers.py b/backend/apps/books/serializers.py index 828d7b2..a5b4fe2 100644 --- a/backend/apps/books/serializers.py +++ b/backend/apps/books/serializers.py @@ -1,6 +1,26 @@ from rest_framework import serializers +import logging + from apps.books.models import Book, BookChapter, EBook, ReadingProgress, ReadingStatus, DownloadRecord +from apps.books.services.ebook_metadata import subjects_from_ebook +from apps.books.services.metadata import enrich_ebook_metadata +from apps.books.services.process_ebook import apply_processing_to_ebook +from apps.reader.models import ReadingSettings + +logger = logging.getLogger(__name__) + +class BookReadingSettingsSerializer(serializers.ModelSerializer): + font_style = serializers.CharField(source="font_family") + + class Meta: + model = ReadingSettings + fields = ["font_size", "font_style", "background_color"] + + def validate_font_size(self, value: int) -> int: + if value < 12 or value > 36: + raise serializers.ValidationError("Font size must be between 12 and 36.") + return value class BookChapterSerializer(serializers.ModelSerializer): @@ -51,10 +71,18 @@ class EBookListSerializer(serializers.ModelSerializer): filename = serializers.CharField(read_only=True) format = serializers.CharField(read_only=True) progress = serializers.SerializerMethodField() + started = serializers.SerializerMethodField() + subjects = serializers.SerializerMethodField() class Meta: model = EBook - fields = ["id", "title", "author", "filename", "format", "page_count", "file_size", "cover_image", "created_at", "progress"] + fields = [ + "id", "title", "author", "filename", "format", "page_count", "file_size", + "cover_image", "created_at", "progress", "started", "subjects", + ] + + def get_subjects(self, obj: EBook) -> list[str]: + return subjects_from_ebook(obj) def get_progress(self, obj): try: @@ -62,16 +90,32 @@ class EBookListSerializer(serializers.ModelSerializer): except ReadingProgress.DoesNotExist: return None + def get_started(self, obj): + """True when the user has opened the reader with saved progress.""" + try: + rp = obj.reading_progress + except ReadingProgress.DoesNotExist: + return False + if rp.current_position >= 99: + return False + if obj.format == "pdf": + return rp.last_page > 0 or rp.current_position > 0 + return bool((rp.epub_location or "").strip()) + class EBookDetailSerializer(serializers.ModelSerializer): filename = serializers.CharField(read_only=True) format = serializers.CharField(read_only=True) file_url = serializers.SerializerMethodField() progress = serializers.SerializerMethodField() + metadata = serializers.JSONField(source="metadata_json", read_only=True) class Meta: model = EBook - fields = ["id", "title", "author", "filename", "format", "page_count", "file_size", "file_url", "cover_image", "created_at", "updated_at", "progress"] + fields = [ + "id", "title", "author", "filename", "format", "page_count", "file_size", + "file_url", "cover_image", "metadata", "created_at", "updated_at", "progress", + ] def get_file_url(self, obj): request = self.context.get("request") @@ -82,7 +126,11 @@ class EBookDetailSerializer(serializers.ModelSerializer): def get_progress(self, obj): try: rp = obj.reading_progress - return {"current_position": rp.current_position, "last_page": rp.last_page} + return { + "current_position": rp.current_position, + "last_page": rp.last_page, + "epub_location": rp.epub_location, + } except ReadingProgress.DoesNotExist: return None @@ -103,22 +151,35 @@ class EBookUploadSerializer(serializers.ModelSerializer): return value def create(self, validated_data): - validated_data["user"] = self.context["request"].user - # Auto-detect format from file extension import os + + validated_data["user"] = self.context["request"].user name = str(getattr(validated_data.get("file"), "name", "")) ext = os.path.splitext(name)[1].lower().lstrip(".") if ext: validated_data["format"] = ext - return super().create(validated_data) + ebook = super().create(validated_data) + try: + apply_processing_to_ebook(ebook) + except Exception: + logger.exception("E-book processing failed for ebook %s", ebook.pk) + try: + enrich_ebook_metadata(ebook) + except Exception: + logger.exception("Metadata enrichment failed for ebook %s", ebook.pk) + ebook.refresh_from_db() + return ebook class ReadingProgressSerializer(serializers.ModelSerializer): class Meta: model = ReadingProgress - fields = ["current_position", "last_page", "device_id", "device_name", "version", "updated_at"] + fields = [ + "current_position", "last_page", "epub_location", + "device_id", "device_name", "version", "updated_at", + ] read_only_fields = ["version", "updated_at"] - extra_kwargs = {"current_position": {"required": True, "min_value": 0.0, "max_value": 100.0}} + extra_kwargs = {"current_position": {"required": False, "min_value": 0.0, "max_value": 100.0}} def validate_current_position(self, value): if value < 0.0 or value > 100.0: @@ -155,7 +216,11 @@ class DownloadRecordSerializer(serializers.ModelSerializer): def get_progress(self, obj): try: rp = obj.ebook.reading_progress - return {"current_position": rp.current_position, "last_page": rp.last_page} + return { + "current_position": rp.current_position, + "last_page": rp.last_page, + "epub_location": rp.epub_location, + } except ReadingProgress.DoesNotExist: return None diff --git a/backend/apps/books/services/__init__.py b/backend/apps/books/services/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/apps/books/services/ebook_metadata.py b/backend/apps/books/services/ebook_metadata.py new file mode 100644 index 0000000..cd0e7d3 --- /dev/null +++ b/backend/apps/books/services/ebook_metadata.py @@ -0,0 +1,8 @@ +from __future__ import annotations + +from apps.books.models import EBook + + +def subjects_from_ebook(ebook: EBook) -> list[str]: + ol = (ebook.metadata_json or {}).get("openlibrary") or {} + return [s.strip() for s in (ol.get("subjects") or []) if isinstance(s, str) and s.strip()] diff --git a/backend/apps/books/services/metadata.py b/backend/apps/books/services/metadata.py new file mode 100644 index 0000000..909c691 --- /dev/null +++ b/backend/apps/books/services/metadata.py @@ -0,0 +1,78 @@ +from __future__ import annotations + +import logging +from datetime import datetime, timezone + +from django.core.files.base import ContentFile + +from apps.books.models import EBook +from apps.books.services.openlibrary import ( + HIGH_CONFIDENCE, + download_cover, + fetch_metadata, +) + +logger = logging.getLogger(__name__) + + +def enrich_ebook_metadata(ebook: EBook) -> EBook: + """Fetch Open Library metadata and update the ebook. Never raises to callers.""" + user_title = ebook.title + user_author = ebook.author or "" + + try: + result = fetch_metadata(user_title, user_author) + except Exception: + logger.exception("Open Library metadata fetch failed for ebook %s", ebook.pk) + return ebook + + if result is None: + return ebook + + now = datetime.now(timezone.utc).isoformat() + metadata = { + "source": "openlibrary", + "matched_at": now, + "match_language": result.match_language, + "match_score": result.match_score, + "match_status": result.match_status, + "user_input": {"title": user_title, "author": user_author}, + "openlibrary": result.openlibrary, + } + ebook.metadata_json = metadata + + update_fields = ["metadata_json", "updated_at"] + + if result.match_status == "not_found": + ebook.save(update_fields=update_fields) + return ebook + + ol = result.openlibrary + cover_id = ol.get("cover_id") + + cover_missing = ( + not ebook.cover_image + or not ebook.cover_image.name + or not ebook.cover_image.storage.exists(ebook.cover_image.name) + ) + if cover_id and cover_missing: + if ebook.cover_image: + ebook.cover_image.delete(save=False) + cover_bytes = download_cover(int(cover_id)) + if cover_bytes: + filename = f"ol_cover_{ebook.pk}_{cover_id}.jpg" + ebook.cover_image.save(filename, ContentFile(cover_bytes), save=False) + update_fields.append("cover_image") + + if result.match_score >= HIGH_CONFIDENCE: + ol_title = ol.get("title") + ol_authors = ol.get("authors") or [] + if ol_title: + ebook.title = ol_title[:512] + update_fields.append("title") + if ol_authors: + ebook.author = ol_authors[0][:256] + update_fields.append("author") + + ebook.save(update_fields=list(dict.fromkeys(update_fields))) + return ebook diff --git a/backend/apps/books/services/openlibrary.py b/backend/apps/books/services/openlibrary.py new file mode 100644 index 0000000..efdb0b8 --- /dev/null +++ b/backend/apps/books/services/openlibrary.py @@ -0,0 +1,304 @@ +from __future__ import annotations + +import logging +import re +import unicodedata +from dataclasses import dataclass +from difflib import SequenceMatcher +from typing import Any + +import httpx +from config.settings import settings + +logger = logging.getLogger(__name__) + +SEARCH_URL = "https://openlibrary.org/search.json" +COVERS_URL = "https://covers.openlibrary.org/b/id/{cover_id}-{size}.jpg" +SEARCH_FIELDS = ( + "key,title,author_name,cover_i,first_publish_year,subject,language," + "edition_key,number_of_pages_median,publisher" +) + +HIGH_CONFIDENCE = 0.8 +LOW_CONFIDENCE = 0.6 + +# EPUB release noise often copied from filenames, e.g. "Title [6494] (r2.3)" +_TITLE_NOISE_PATTERNS = ( + re.compile(r"\s*\[\d+\]"), # [6494] + re.compile(r"\s*\([rv][\d.]+\)", re.IGNORECASE), # (r2.3), (v1.0) + re.compile(r"\s*\(rev[\d.]*\)", re.IGNORECASE), # (rev2) +) + + +def _sanitize_search_title(title: str) -> str: + cleaned = title.strip() + for pattern in _TITLE_NOISE_PATTERNS: + cleaned = pattern.sub("", cleaned) + return re.sub(r"\s+", " ", cleaned).strip() + + +@dataclass(frozen=True) +class OpenLibraryHit: + work_key: str + title: str + authors: list[str] + cover_id: int | None + first_publish_year: int | None + subjects: list[str] + languages: list[str] + publishers: list[str] + edition_key: str | None + number_of_pages_median: int | None + match_language: str + score: float + + +@dataclass(frozen=True) +class OpenLibraryMetadata: + match_language: str + match_score: float + match_status: str + openlibrary: dict[str, Any] + + +def _normalize(text: str) -> str: + cleaned = re.sub(r"[^\w\s]", " ", _strip_accents(text).lower()) + return re.sub(r"\s+", " ", cleaned).strip() + + +def _strip_accents(text: str) -> str: + normalized = unicodedata.normalize("NFKD", text) + return "".join(ch for ch in normalized if not unicodedata.combining(ch)) + + +def _build_search_params( + title: str, + author: str, + *, + lang: str | None, + search_mode: str, +) -> dict[str, str | int]: + title = title.strip() + author = author.strip() + params: dict[str, str | int] = {"limit": 10, "fields": SEARCH_FIELDS} + + if search_mode == "spanish_q": + q_parts = ["language:spa", title] + if author: + q_parts.append(author) + params["q"] = " ".join(q_parts) + elif search_mode == "q": + params["q"] = " ".join(part for part in (title, author) if part) + elif search_mode == "q_unaccent": + params["q"] = " ".join( + part for part in (_strip_accents(title), _strip_accents(author) if author else "") if part + ) + else: + params["title"] = title + if author: + params["author"] = author + + if lang: + params["lang"] = lang + return params + + +def _title_similarity(a: str, b: str) -> float: + na, nb = _normalize(a), _normalize(b) + if not na or not nb: + return 0.0 + if na in nb or nb in na: + return 1.0 + return SequenceMatcher(None, na, nb).ratio() + + +def _author_overlap(user_author: str, ol_authors: list[str]) -> float: + if not user_author.strip(): + return 0.5 if ol_authors else 0.0 + user_tokens = set(_normalize(user_author).split()) + if not user_tokens: + return 0.0 + best = 0.0 + for name in ol_authors: + name_tokens = set(_normalize(name).split()) + if not name_tokens: + continue + overlap = len(user_tokens & name_tokens) / len(user_tokens) + best = max(best, overlap) + if user_tokens <= name_tokens or name_tokens <= user_tokens: + best = max(best, 0.95) + return best + + +def _score_hit(title: str, author: str, doc: dict[str, Any], *, lang: str) -> float: + ol_title = doc.get("title") or "" + ol_authors = doc.get("author_name") or [] + title_score = _title_similarity(title, ol_title) + author_score = _author_overlap(author, ol_authors) + combined = (title_score * 0.6) + (author_score * 0.4) + if doc.get("cover_i"): + combined += 0.05 + return min(combined, 1.0) + + +def _parse_hit(doc: dict[str, Any], *, lang: str, score: float) -> OpenLibraryHit: + edition_keys = doc.get("edition_key") or [] + edition_key = edition_keys[0] if edition_keys else None + cover_id = doc.get("cover_i") + return OpenLibraryHit( + work_key=doc.get("key") or "", + title=doc.get("title") or "", + authors=list(doc.get("author_name") or []), + cover_id=int(cover_id) if cover_id else None, + first_publish_year=doc.get("first_publish_year"), + subjects=list(doc.get("subject") or [])[:10], + languages=list(doc.get("language") or []), + publishers=list(doc.get("publisher") or [])[:5], + edition_key=edition_key, + number_of_pages_median=doc.get("number_of_pages_median"), + match_language=lang, + score=score, + ) + + +def build_cover_url(cover_id: int, size: str = "L") -> str: + return COVERS_URL.format(cover_id=cover_id, size=size) + + +def _client() -> httpx.Client: + read_timeout = settings.OPENLIBRARY_TIMEOUT_SECONDS + connect_timeout = settings.OPENLIBRARY_CONNECT_TIMEOUT_SECONDS + return httpx.Client( + timeout=httpx.Timeout( + connect=connect_timeout, + read=read_timeout, + write=read_timeout, + pool=connect_timeout, + ), + headers={"User-Agent": settings.OPENLIBRARY_USER_AGENT}, + follow_redirects=True, + ) + + +def search_works( + title: str, + author: str, + *, + lang: str | None = None, + search_mode: str = "title", + client: httpx.Client | None = None, +) -> list[OpenLibraryHit]: + if not title.strip(): + return [] + + params = _build_search_params(title, author, lang=lang, search_mode=search_mode) + + try: + if client is not None: + response = client.get(SEARCH_URL, params=params) + response.raise_for_status() + docs = response.json().get("docs") or [] + else: + with _client() as owned_client: + response = owned_client.get(SEARCH_URL, params=params) + response.raise_for_status() + docs = response.json().get("docs") or [] + except (httpx.HTTPError, ValueError) as exc: + logger.warning("Open Library search failed: %s", exc) + return [] + + hits: list[OpenLibraryHit] = [] + for doc in docs: + score = _score_hit(title, author, doc, lang=lang or "") + if score < LOW_CONFIDENCE: + continue + hits.append(_parse_hit(doc, lang=lang or "", score=score)) + hits.sort(key=lambda h: (h.score, h.cover_id is not None), reverse=True) + return hits + + +def pick_best_match(title: str, author: str, hits: list[OpenLibraryHit]) -> OpenLibraryHit | None: + return hits[0] if hits else None + + +def download_cover(cover_id: int) -> bytes | None: + url = build_cover_url(cover_id, size="L") + try: + with _client() as client: + response = client.get(url) + if response.status_code == 404: + return None + response.raise_for_status() + content_type = response.headers.get("content-type", "") + if not content_type.startswith("image/"): + return None + return response.content + except httpx.HTTPError as exc: + logger.warning("Open Library cover download failed for %s: %s", cover_id, exc) + return None + + +def _hit_to_openlibrary_dict(hit: OpenLibraryHit) -> dict[str, Any]: + return { + "work_key": hit.work_key, + "edition_key": hit.edition_key, + "title": hit.title, + "authors": hit.authors, + "cover_id": hit.cover_id, + "cover_url": build_cover_url(hit.cover_id) if hit.cover_id else None, + "first_publish_year": hit.first_publish_year, + "subjects": hit.subjects, + "languages": hit.languages, + "publishers": hit.publishers, + "number_of_pages_median": hit.number_of_pages_median, + } + + +def fetch_metadata(title: str, author: str) -> OpenLibraryMetadata | None: + if not settings.OPENLIBRARY_ENABLED: + return None + + preferred = settings.OPENLIBRARY_PREFERRED_LANG + fallback = settings.OPENLIBRARY_FALLBACK_LANG + search_title = _sanitize_search_title(title) + + if not search_title: + return OpenLibraryMetadata( + match_language=preferred, + match_score=0.0, + match_status="not_found", + openlibrary={}, + ) + + search_plan: list[tuple[str, str | None]] = [ + ("spanish_q", preferred), + ("title", preferred), + ("q", None), + ("q_unaccent", None), + ] + if fallback != preferred: + search_plan.extend([("title", fallback), ("q", fallback)]) + + hits: list[OpenLibraryHit] = [] + with _client() as client: + for search_mode, lang in search_plan: + hits = search_works(search_title, author, lang=lang, search_mode=search_mode, client=client) + if hits: + break + + hit = pick_best_match(title, author, hits) + if not hit: + return OpenLibraryMetadata( + match_language=preferred, + match_score=0.0, + match_status="not_found", + openlibrary={}, + ) + + status = "matched" if hit.score >= HIGH_CONFIDENCE else "partial" + return OpenLibraryMetadata( + match_language=hit.match_language, + match_score=hit.score, + match_status=status, + openlibrary=_hit_to_openlibrary_dict(hit), + ) diff --git a/backend/apps/books/services/process_ebook.py b/backend/apps/books/services/process_ebook.py new file mode 100644 index 0000000..1465408 --- /dev/null +++ b/backend/apps/books/services/process_ebook.py @@ -0,0 +1,239 @@ +"""Extract TOC, metadata, and page count from uploaded EPUB/PDF files.""" + +from __future__ import annotations + +import logging +import os +from pathlib import Path +from typing import Any + +from apps.books.models import BookChapter, EBook + +logger = logging.getLogger(__name__) + + +def process_ebook(file_path: str, original_filename: str | None = None) -> dict[str, Any]: + """Parse an e-book file and return format, metadata, TOC, and page count.""" + ext = Path(original_filename or file_path).suffix.lower() + if ext == ".epub" or file_path.lower().endswith(".epub"): + return _process_epub(file_path) + if ext == ".pdf" or file_path.lower().endswith(".pdf"): + return _process_pdf(file_path) + return {"format": ext.lstrip(".") or "unknown", "page_count": 0, "metadata": {}, "toc": []} + + +def store_chapters(ebook: EBook, toc: list[dict[str, Any]], parent_index: int = 0) -> None: + """Recursively store TOC entries as BookChapter records.""" + for idx, entry in enumerate(toc): + BookChapter.objects.create( + ebook=ebook, + title=entry.get("title", "Untitled"), + index=parent_index + idx, + href=entry.get("href", ""), + children=entry.get("children", []), + ) + children = entry.get("children", []) + if children: + store_chapters(ebook, children, parent_index + idx + 1) + + +def apply_processing_to_ebook(ebook: EBook) -> dict[str, Any]: + """Run processing on an EBook instance and persist chapters + metadata.""" + if not ebook.file: + raise ValueError("No file found for this e-book.") + + file_path = ebook.file.path + result = process_ebook(file_path, original_filename=ebook.filename()) + + ebook.format = result.get("format", ebook.format) + ebook.page_count = result.get("page_count", 0) + file_metadata = result.get("metadata") or {} + if file_metadata: + merged = dict(ebook.metadata_json or {}) + merged["file"] = file_metadata + ebook.metadata_json = merged + ebook.save(update_fields=["format", "page_count", "metadata_json", "updated_at"]) + + raw_toc: list[dict[str, Any]] = result.get("toc", []) + BookChapter.objects.filter(ebook=ebook).delete() + store_chapters(ebook, raw_toc) + + return { + "format": ebook.format, + "page_count": ebook.page_count, + "metadata": ebook.metadata_json, + "toc_count": len(raw_toc), + "status": "processed", + } + + +def _process_epub(file_path: str) -> dict[str, Any]: + from ebooklib import epub, ITEM_DOCUMENT + + book = epub.read_epub(file_path) + metadata = _extract_epub_metadata(book) + toc = _extract_epub_toc(book) + + if not toc: + toc = _fallback_toc_from_spine(book, ITEM_DOCUMENT) + + flat_count = _count_toc_entries(toc) + page_count = flat_count or len(book.spine) + + return { + "format": "epub", + "page_count": page_count, + "metadata": metadata, + "toc": toc, + } + + +def _process_pdf(file_path: str) -> dict[str, Any]: + from pypdf import PdfReader + + reader = PdfReader(file_path) + page_count = len(reader.pages) + metadata = _extract_pdf_metadata(reader) + toc = _extract_pdf_outline(reader) + if not toc and page_count > 0: + toc = [ + {"title": f"Page {i}", "href": f"pdf:page:{i}", "children": []} + for i in range(1, page_count + 1) + ] + return { + "format": "pdf", + "page_count": page_count, + "metadata": metadata, + "toc": toc, + } + + +def _extract_pdf_metadata(reader) -> dict[str, Any]: + metadata: dict[str, Any] = {} + info = reader.metadata + if not info: + return metadata + title = getattr(info, "title", None) + author = getattr(info, "author", None) + if title: + metadata["title"] = str(title) + if author: + metadata["author"] = str(author) + return metadata + + +def _extract_pdf_outline(reader) -> list[dict[str, Any]]: + outline = getattr(reader, "outline", None) + if not outline: + return [] + return _walk_pdf_outline(reader, outline) + + +def _walk_pdf_outline(reader, outline: list[Any]) -> list[dict[str, Any]]: + entries: list[dict[str, Any]] = [] + i = 0 + while i < len(outline): + item = outline[i] + if isinstance(item, list): + if entries: + entries[-1]["children"] = _walk_pdf_outline(reader, item) + i += 1 + continue + title = getattr(item, "title", None) or "Section" + page_num = 1 + try: + page_num = reader.get_destination_page_number(item) + 1 + except Exception: + logger.debug("Could not resolve PDF outline destination", exc_info=True) + entries.append({ + "title": str(title), + "href": f"pdf:page:{page_num}", + "children": [], + }) + i += 1 + return entries + + +def _extract_epub_metadata(book) -> dict[str, Any]: + metadata: dict[str, Any] = {} + + def first(namespace: str, name: str) -> str: + values = book.get_metadata(namespace, name) + if values: + return str(values[0][0]) + return "" + + title = first("DC", "title") + if title: + metadata["title"] = title + creator = first("DC", "creator") + if creator: + metadata["author"] = creator + language = first("DC", "language") + if language: + metadata["language"] = language + identifier = first("DC", "identifier") + if identifier: + metadata["identifier"] = identifier + return metadata + + +def _parse_toc_item(item) -> dict[str, Any]: + from ebooklib import epub + + if isinstance(item, epub.Link): + return { + "title": item.title or "Untitled", + "href": item.href or "", + "children": [], + } + if isinstance(item, tuple): + section, children = item + entry = { + "title": getattr(section, "title", None) or "Untitled", + "href": getattr(section, "href", None) or "", + "children": [], + } + for child in children: + entry["children"].append(_parse_toc_item(child)) + return entry + if hasattr(item, "title"): + return { + "title": item.title or "Untitled", + "href": getattr(item, "href", "") or "", + "children": [], + } + return {"title": "Untitled", "href": "", "children": []} + + +def _extract_epub_toc(book) -> list[dict[str, Any]]: + return [_parse_toc_item(item) for item in book.toc] + + +def _fallback_toc_from_spine(book, item_document_type) -> list[dict[str, Any]]: + toc: list[dict[str, Any]] = [] + seen: set[str] = set() + chapter_num = 0 + + for spine_entry in book.spine: + item_id = spine_entry[0] if isinstance(spine_entry, tuple) else spine_entry + item = book.get_item_with_id(item_id) + if not item or item.get_type() != item_document_type: + continue + href = item.get_name() or "" + if not href or href in seen: + continue + seen.add(href) + chapter_num += 1 + title = os.path.splitext(os.path.basename(href))[0] or f"Chapter {chapter_num}" + toc.append({"title": title.replace("_", " ").replace("-", " "), "href": href, "children": []}) + + return toc + + +def _count_toc_entries(toc: list[dict[str, Any]]) -> int: + count = 0 + for entry in toc: + count += 1 + count += _count_toc_entries(entry.get("children", [])) + return count diff --git a/backend/apps/books/urls.py b/backend/apps/books/urls.py index e03cfb3..730259c 100644 --- a/backend/apps/books/urls.py +++ b/backend/apps/books/urls.py @@ -1,15 +1,13 @@ from django.urls import include, path from rest_framework.routers import DefaultRouter -from apps.books.views import BookViewSet, EBookViewSet +from apps.books.views import BookViewSet, EBookViewSet, book_reading_settings_view router = DefaultRouter() +router.register(r"ebooks", EBookViewSet, basename="ebook") router.register(r"", BookViewSet, basename="book") -ebook_router = DefaultRouter() -ebook_router.register(r"ebooks", EBookViewSet, basename="ebook") - urlpatterns = [ + path("settings/", book_reading_settings_view, name="book-settings"), path("", include(router.urls)), - path("", include(ebook_router.urls)), -] \ No newline at end of file +] diff --git a/backend/apps/books/views.py b/backend/apps/books/views.py index 63e34a8..de08a53 100644 --- a/backend/apps/books/views.py +++ b/backend/apps/books/views.py @@ -4,9 +4,10 @@ import logging from typing import Any from django.db.models import QuerySet, Q +from django.http import FileResponse from django_filters.rest_framework import DjangoFilterBackend from rest_framework import parsers, permissions, status, viewsets -from rest_framework.decorators import action +from rest_framework.decorators import action, api_view, permission_classes from rest_framework.filters import OrderingFilter, SearchFilter from rest_framework.permissions import AllowAny, IsAuthenticated from rest_framework.request import Request @@ -14,11 +15,14 @@ from rest_framework.response import Response from apps.books.models import Book, BookChapter, DownloadRecord, EBook, ReadingProgress from apps.books.serializers import ( - BookChapterSerializer, BookDetailSerializer, BookListSerializer, BookSerializer, - DownloadRecordSerializer, EBookContentSerializer, EBookDetailSerializer, + BookChapterSerializer, BookDetailSerializer, BookListSerializer, BookReadingSettingsSerializer, + BookSerializer, DownloadRecordSerializer, EBookContentSerializer, EBookDetailSerializer, EBookListSerializer, EBookTocSerializer, EBookUploadSerializer, ReadingProgressSerializer, StorageSummarySerializer, ) +from apps.reader.models import ReadingSettings +from apps.books.services.ebook_metadata import subjects_from_ebook +from apps.books.services.metadata import enrich_ebook_metadata logger = logging.getLogger(__name__) @@ -93,7 +97,30 @@ class EBookViewSet(viewsets.ModelViewSet): return EBookDetailSerializer def get_queryset(self): - return EBook.objects.filter(user=self.request.user).select_related("reading_progress", "user") + qs = EBook.objects.filter(user=self.request.user).select_related("reading_progress", "user") + query = self.request.query_params.get("q", "").strip() + if query and self.action == "list": + qs = qs.filter( + Q(title__icontains=query) | Q(author__icontains=query), + ) + return qs + + @action(detail=False, methods=["get"]) + def genres(self, request: Request) -> Response: + genre_set: set[str] = set() + for ebook in self.get_queryset(): + genre_set.update(subjects_from_ebook(ebook)) + return Response(sorted(genre_set)) + + @action(detail=False, methods=["get"]) + def authors(self, request: Request) -> Response: + author_list = ( + self.get_queryset() + .values_list("author", flat=True) + .distinct() + .order_by("author") + ) + return Response([a for a in author_list if a]) @action(detail=True, methods=["get", "patch"]) def progress(self, request: Request, pk: int | None = None) -> Response: @@ -107,6 +134,22 @@ class EBookViewSet(viewsets.ModelViewSet): serializer.save() return Response(serializer.data) + @action(detail=True, methods=["post"], url_path="enrich-metadata") + def enrich_metadata(self, request: Request, pk: int | None = None) -> Response: + """Re-fetch Open Library metadata and cover for this ebook.""" + ebook = self.get_object() + try: + enrich_ebook_metadata(ebook) + except Exception: + logger.exception("Manual metadata enrichment failed for ebook %s", ebook.id) + return Response( + {"error": "Metadata enrichment failed."}, + status=status.HTTP_502_BAD_GATEWAY, + ) + ebook.refresh_from_db() + serializer = EBookDetailSerializer(ebook, context={"request": request}) + return Response(serializer.data) + @action(detail=True, methods=["post"]) def process(self, request: Request, pk: int | None = None) -> Response: """Trigger e-book processing: metadata extraction, TOC building, page counting.""" @@ -115,33 +158,36 @@ class EBookViewSet(viewsets.ModelViewSet): return Response({"error": "No file found for this e-book."}, status=status.HTTP_400_BAD_REQUEST) try: - from apps.books.services import process_ebook + from apps.books.services.process_ebook import apply_processing_to_ebook - file_path = ebook.file.path - result = process_ebook(file_path, original_filename=ebook.filename()) - - # Update ebook with extracted data - ebook.format = result.get("format", ebook.format) - ebook.page_count = result.get("page_count", 0) - ebook.metadata_json = result.get("metadata", {}) - ebook.save(update_fields=["format", "page_count", "metadata_json", "updated_at"]) - - # Store chapters in DB - raw_toc: list[dict[str, Any]] = result.get("toc", []) - BookChapter.objects.filter(ebook=ebook).delete() - _store_chapters(ebook, raw_toc) - - return Response({ - "format": ebook.format, - "page_count": ebook.page_count, - "metadata": ebook.metadata_json, - "toc_count": len(raw_toc), - "status": "processed", - }) + result = apply_processing_to_ebook(ebook) + return Response(result) except Exception as exc: logger.exception("Failed to process ebook %s", ebook.id) return Response({"error": f"Processing failed: {exc}"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + @action(detail=True, methods=["get"]) + def file(self, request: Request, pk: int | None = None) -> FileResponse | Response: + """Stream the ebook file for authenticated in-browser reading.""" + ebook = self.get_object() + content_types = { + "epub": "application/epub+zip", + "pdf": "application/pdf", + } + content_type = content_types.get(ebook.format) + if not content_type: + return Response( + {"error": "Unsupported format for in-browser reading."}, + status=status.HTTP_400_BAD_REQUEST, + ) + if not ebook.file: + return Response({"error": "No file found for this e-book."}, status=status.HTTP_400_BAD_REQUEST) + return FileResponse( + ebook.file.open("rb"), + content_type=content_type, + filename=ebook.filename(), + ) + @action(detail=True, methods=["get"]) def toc(self, request: Request, pk: int | None = None) -> Response: """Return hierarchical table of contents.""" @@ -229,21 +275,6 @@ class EBookViewSet(viewsets.ModelViewSet): return Response(status=status.HTTP_204_NO_CONTENT) -def _store_chapters(ebook: EBook, toc: list[dict[str, Any]], parent_index: int = 0) -> None: - """Recursively store TOC entries as BookChapter records.""" - for idx, entry in enumerate(toc): - BookChapter.objects.create( - ebook=ebook, - title=entry.get("title", "Untitled"), - index=parent_index + idx, - href=entry.get("href", ""), - children=entry.get("children", []), - ) - children = entry.get("children", []) - if children: - _store_chapters(ebook, children, parent_index + idx + 1) - - def _fetch_chapter_content(ebook: EBook, chapter: BookChapter) -> str: """Fetch HTML content for a chapter from the e-book file.""" if ebook.format == "epub": @@ -276,4 +307,20 @@ def _fetch_epub_chapter_content(file_path: str, chapter: BookChapter) -> str: return "" except Exception: logger.exception("Failed to fetch EPUB chapter content for %s", chapter.href) - return "" \ No newline at end of file + return "" + + +@api_view(["GET", "PATCH"]) +@permission_classes([IsAuthenticated]) +def book_reading_settings_view(request: Request) -> Response: + """Get or update reading settings via the books API contract.""" + settings, _created = ReadingSettings.objects.get_or_create(user=request.user) + + if request.method == "GET": + serializer = BookReadingSettingsSerializer(settings) + return Response(serializer.data) + + serializer = BookReadingSettingsSerializer(settings, data=request.data, partial=True) + serializer.is_valid(raise_exception=True) + serializer.save() + return Response(serializer.data) \ No newline at end of file diff --git a/backend/apps/users/migrations/0001_initial.py b/backend/apps/users/migrations/0001_initial.py new file mode 100644 index 0000000..c0015c5 --- /dev/null +++ b/backend/apps/users/migrations/0001_initial.py @@ -0,0 +1,44 @@ +# Generated by Django 5.1.7 on 2026-06-03 22:26 + +import django.contrib.auth.models +import django.contrib.auth.validators +import django.utils.timezone +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('auth', '0012_alter_user_first_name_max_length'), + ] + + operations = [ + migrations.CreateModel( + name='User', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('password', models.CharField(max_length=128, verbose_name='password')), + ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), + ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), + ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')), + ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')), + ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')), + ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')), + ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), + ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), + ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), + ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')), + ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')), + ], + options={ + 'verbose_name': 'User', + 'verbose_name_plural': 'Users', + 'db_table': 'users_user', + }, + managers=[ + ('objects', django.contrib.auth.models.UserManager()), + ], + ), + ] diff --git a/backend/apps/users/migrations/__init__.py b/backend/apps/users/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/apps/users/serializers.py b/backend/apps/users/serializers.py new file mode 100644 index 0000000..5131cc6 --- /dev/null +++ b/backend/apps/users/serializers.py @@ -0,0 +1,93 @@ +import re + +from django.contrib.auth import get_user_model +from django.contrib.auth.password_validation import validate_password +from rest_framework import serializers +from rest_framework_simplejwt.serializers import TokenObtainPairSerializer + +User = get_user_model() + + +def _derive_username(email: str) -> str: + local = email.split("@", 1)[0] + candidate = re.sub(r"[^\w.@+-]", "_", local).strip("._") + return candidate[:150] if candidate else "user" + + +def _unique_username(base: str) -> str: + username = base[:150] + if not User.objects.filter(username=username).exists(): + return username + suffix = 1 + while User.objects.filter(username=f"{username[:140]}_{suffix}").exists(): + suffix += 1 + return f"{username[:140]}_{suffix}" + + +class RegisterSerializer(serializers.Serializer): + email = serializers.EmailField() + password = serializers.CharField(write_only=True, min_length=8) + + def validate_email(self, value: str) -> str: + email = value.lower() + if User.objects.filter(email__iexact=email).exists(): + raise serializers.ValidationError("A user with this email already exists.") + return email + + def validate_password(self, value: str) -> str: + validate_password(value) + return value + + def create(self, validated_data: dict) -> User: + email = validated_data["email"] + username = _unique_username(_derive_username(email)) + return User.objects.create_user( + username=username, + email=email, + password=validated_data["password"], + ) + + def to_representation(self, instance: User) -> dict: + return { + "id": instance.id, + "email": instance.email, + "username": instance.username, + } + + +class EmailTokenObtainPairSerializer(TokenObtainPairSerializer): + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + self.fields.pop(self.username_field, None) + self.fields["email"] = serializers.EmailField(required=True) + + def validate(self, attrs: dict) -> dict: + email = attrs.get("email", "").lower() + password = attrs.get("password") + + try: + user = User.objects.get(email__iexact=email) + except User.DoesNotExist as exc: + raise serializers.ValidationError( + {"detail": "No active account found with the given credentials."} + ) from exc + + if not user.check_password(password): + raise serializers.ValidationError( + {"detail": "No active account found with the given credentials."} + ) + + if not user.is_active: + raise serializers.ValidationError({"detail": "User account is disabled."}) + + refresh = self.get_token(user) + return { + "refresh": str(refresh), + "access": str(refresh.access_token), + } + + @classmethod + def get_token(cls, user: User) -> object: + token = super().get_token(user) + token["email"] = user.email + return token diff --git a/backend/apps/users/urls.py b/backend/apps/users/urls.py index 4386a57..c2056be 100644 --- a/backend/apps/users/urls.py +++ b/backend/apps/users/urls.py @@ -1,8 +1,11 @@ -from django.urls import include, path -from rest_framework.routers import DefaultRouter -from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView +from django.urls import path +from rest_framework_simplejwt.views import TokenRefreshView + +from apps.users.views import EmailTokenObtainPairView, RegisterView urlpatterns = [ - path("token/", TokenObtainPairView.as_view(), name="token_obtain_pair"), + path("register/", RegisterView.as_view(), name="register"), + path("token/", EmailTokenObtainPairView.as_view(), name="token_obtain_pair"), + path("login/", EmailTokenObtainPairView.as_view(), name="login"), path("token/refresh/", TokenRefreshView.as_view(), name="token_refresh"), -] \ No newline at end of file +] diff --git a/backend/apps/users/views.py b/backend/apps/users/views.py new file mode 100644 index 0000000..4c107c0 --- /dev/null +++ b/backend/apps/users/views.py @@ -0,0 +1,14 @@ +from rest_framework import generics +from rest_framework.permissions import AllowAny +from rest_framework_simplejwt.views import TokenObtainPairView + +from apps.users.serializers import EmailTokenObtainPairSerializer, RegisterSerializer + + +class RegisterView(generics.CreateAPIView): + serializer_class = RegisterSerializer + permission_classes = [AllowAny] + + +class EmailTokenObtainPairView(TokenObtainPairView): + serializer_class = EmailTokenObtainPairSerializer diff --git a/backend/config/django.py b/backend/config/django.py index 21cf721..4019b7c 100644 --- a/backend/config/django.py +++ b/backend/config/django.py @@ -145,7 +145,7 @@ USE_TZ = True # --------------------------------------------------------------------------- STATIC_URL = "static/" STATIC_ROOT = BASE_DIR / "staticfiles" -MEDIA_URL = "media/" +MEDIA_URL = "/media/" MEDIA_ROOT = BASE_DIR / "media" # Maximum upload size: 50MB diff --git a/backend/config/settings.py b/backend/config/settings.py index 7812b19..6b9d9db 100644 --- a/backend/config/settings.py +++ b/backend/config/settings.py @@ -1,6 +1,3 @@ -import os -from pathlib import Path - from pydantic_settings import BaseSettings @@ -29,6 +26,14 @@ class Settings(BaseSettings): "http://localhost:3000", ] + # Open Library metadata enrichment + OPENLIBRARY_ENABLED: bool = True + OPENLIBRARY_PREFERRED_LANG: str = "es" + OPENLIBRARY_FALLBACK_LANG: str = "en" + OPENLIBRARY_TIMEOUT_SECONDS: float = 10.0 + OPENLIBRARY_CONNECT_TIMEOUT_SECONDS: float = 15.0 + OPENLIBRARY_USER_AGENT: str = "CloudReader/1.0 (https://github.com/cloud-reader)" + @property def DATABASE_URL(self) -> str: return ( @@ -39,4 +44,4 @@ class Settings(BaseSettings): model_config = {"env_file": ".env", "env_file_encoding": "utf-8"} -settings = Settings() \ No newline at end of file +settings = Settings() diff --git a/backend/config/urls.py b/backend/config/urls.py index 9db20f1..07d350e 100644 --- a/backend/config/urls.py +++ b/backend/config/urls.py @@ -1,3 +1,4 @@ +from django.conf import settings from django.contrib import admin from django.urls import include, path @@ -7,4 +8,9 @@ urlpatterns = [ path("api/books/", include("apps.books.urls")), path("api/annotations/", include("apps.annotations.urls")), path("api/reader/", include("apps.reader.urls")), -] \ No newline at end of file +] + +if settings.DEBUG: + from django.conf.urls.static import static + + urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) \ No newline at end of file diff --git a/backend/pyproject.toml b/backend/pyproject.toml new file mode 100644 index 0000000..7d0ae0b --- /dev/null +++ b/backend/pyproject.toml @@ -0,0 +1,25 @@ +[project] +name = "backend" +version = "0.1.0" +requires-python = ">=3.12" +dependencies = [ + "coverage==7.6.10", + "django==5.1.7", + "django-cors-headers==4.6.0", + "django-filter==25.1", + "djangorestframework==3.15.2", + "djangorestframework-simplejwt==5.4.0", + "gunicorn==23.0.0", + "pillow>=11.0.0", + "psycopg2-binary==2.9.10", + "pydantic==2.10.5", + "pydantic-settings==2.7.1", + "pytest==8.3.4", + "pytest-cov==6.0.0", + "pytest-django==4.9.0", + "python-dotenv==1.0.1", + "httpx>=0.28.0", + "ebooklib>=0.18", + "beautifulsoup4>=4.12.0", + "pypdf>=5.0.0", +] diff --git a/backend/uv.lock b/backend/uv.lock new file mode 100644 index 0000000..2240ebe --- /dev/null +++ b/backend/uv.lock @@ -0,0 +1,673 @@ +version = 1 +revision = 3 +requires-python = ">=3.12" + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, +] + +[[package]] +name = "anyio" +version = "4.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622, upload-time = "2026-03-24T12:59:09.671Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353, upload-time = "2026-03-24T12:59:08.246Z" }, +] + +[[package]] +name = "asgiref" +version = "3.11.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/63/40/f03da1264ae8f7cfdbf9146542e5e7e8100a4c66ab48e791df9a03d3f6c0/asgiref-3.11.1.tar.gz", hash = "sha256:5f184dc43b7e763efe848065441eac62229c9f7b0475f41f80e207a114eda4ce", size = 38550, upload-time = "2026-02-03T13:30:14.33Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/0a/a72d10ed65068e115044937873362e6e32fab1b7dce0046aeb224682c989/asgiref-3.11.1-py3-none-any.whl", hash = "sha256:e8667a091e69529631969fd45dc268fa79b99c92c5fcdda727757e52146ec133", size = 24345, upload-time = "2026-02-03T13:30:13.039Z" }, +] + +[[package]] +name = "backend" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "beautifulsoup4" }, + { name = "coverage" }, + { name = "django" }, + { name = "django-cors-headers" }, + { name = "django-filter" }, + { name = "djangorestframework" }, + { name = "djangorestframework-simplejwt" }, + { name = "ebooklib" }, + { name = "gunicorn" }, + { name = "httpx" }, + { name = "pillow" }, + { name = "psycopg2-binary" }, + { name = "pydantic" }, + { name = "pydantic-settings" }, + { name = "pypdf" }, + { name = "pytest" }, + { name = "pytest-cov" }, + { name = "pytest-django" }, + { name = "python-dotenv" }, +] + +[package.metadata] +requires-dist = [ + { name = "beautifulsoup4", specifier = ">=4.12.0" }, + { name = "coverage", specifier = "==7.6.10" }, + { name = "django", specifier = "==5.1.7" }, + { name = "django-cors-headers", specifier = "==4.6.0" }, + { name = "django-filter", specifier = "==25.1" }, + { name = "djangorestframework", specifier = "==3.15.2" }, + { name = "djangorestframework-simplejwt", specifier = "==5.4.0" }, + { name = "ebooklib", specifier = ">=0.18" }, + { name = "gunicorn", specifier = "==23.0.0" }, + { name = "httpx", specifier = ">=0.28.0" }, + { name = "pillow", specifier = ">=11.0.0" }, + { name = "psycopg2-binary", specifier = "==2.9.10" }, + { name = "pydantic", specifier = "==2.10.5" }, + { name = "pydantic-settings", specifier = "==2.7.1" }, + { name = "pypdf", specifier = ">=5.0.0" }, + { name = "pytest", specifier = "==8.3.4" }, + { name = "pytest-cov", specifier = "==6.0.0" }, + { name = "pytest-django", specifier = "==4.9.0" }, + { name = "python-dotenv", specifier = "==1.0.1" }, +] + +[[package]] +name = "beautifulsoup4" +version = "4.14.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "soupsieve" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c3/b0/1c6a16426d389813b48d95e26898aff79abbde42ad353958ad95cc8c9b21/beautifulsoup4-4.14.3.tar.gz", hash = "sha256:6292b1c5186d356bba669ef9f7f051757099565ad9ada5dd630bd9de5fa7fb86", size = 627737, upload-time = "2025-11-30T15:08:26.084Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1a/39/47f9197bdd44df24d67ac8893641e16f386c984a0619ef2ee4c51fbbc019/beautifulsoup4-4.14.3-py3-none-any.whl", hash = "sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb", size = 107721, upload-time = "2025-11-30T15:08:24.087Z" }, +] + +[[package]] +name = "certifi" +version = "2026.5.20" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/ce/ee2ecad540810a79593028e88299baeae54d346cc7a0d94b6199988b89b1/certifi-2026.5.20.tar.gz", hash = "sha256:69dea482ab64caa7b9f6aba1c6bf48bb6a5448d1c0f1b17ab42ad8c763a5344d", size = 135422, upload-time = "2026-05-20T11:46:50.073Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/59/8c/57e832b7af6d7c5abe66eb3fbe3a3a32f4d11ea23a1aa7131371035be991/certifi-2026.5.20-py3-none-any.whl", hash = "sha256:3c52e209ba0a4ad7aebe60436a4ab349c39e1e602e8c134221e546902ad25897", size = 134134, upload-time = "2026-05-20T11:46:48.578Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "coverage" +version = "7.6.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/84/ba/ac14d281f80aab516275012e8875991bb06203957aa1e19950139238d658/coverage-7.6.10.tar.gz", hash = "sha256:7fb105327c8f8f0682e29843e2ff96af9dcbe5bab8eeb4b398c6a33a16d80a23", size = 803868, upload-time = "2024-12-26T16:59:18.734Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/86/77/19d09ea06f92fdf0487499283b1b7af06bc422ea94534c8fe3a4cd023641/coverage-7.6.10-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:27c6e64726b307782fa5cbe531e7647aee385a29b2107cd87ba7c0105a5d3853", size = 208281, upload-time = "2024-12-26T16:57:42.968Z" }, + { url = "https://files.pythonhosted.org/packages/b6/67/5479b9f2f99fcfb49c0d5cf61912a5255ef80b6e80a3cddba39c38146cf4/coverage-7.6.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c56e097019e72c373bae32d946ecf9858fda841e48d82df7e81c63ac25554078", size = 208514, upload-time = "2024-12-26T16:57:45.747Z" }, + { url = "https://files.pythonhosted.org/packages/15/d1/febf59030ce1c83b7331c3546d7317e5120c5966471727aa7ac157729c4b/coverage-7.6.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7827a5bc7bdb197b9e066cdf650b2887597ad124dd99777332776f7b7c7d0d0", size = 241537, upload-time = "2024-12-26T16:57:48.647Z" }, + { url = "https://files.pythonhosted.org/packages/4b/7e/5ac4c90192130e7cf8b63153fe620c8bfd9068f89a6d9b5f26f1550f7a26/coverage-7.6.10-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:204a8238afe787323a8b47d8be4df89772d5c1e4651b9ffa808552bdf20e1d50", size = 238572, upload-time = "2024-12-26T16:57:51.668Z" }, + { url = "https://files.pythonhosted.org/packages/dc/03/0334a79b26ecf59958f2fe9dd1f5ab3e2f88db876f5071933de39af09647/coverage-7.6.10-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e67926f51821b8e9deb6426ff3164870976fe414d033ad90ea75e7ed0c2e5022", size = 240639, upload-time = "2024-12-26T16:57:53.175Z" }, + { url = "https://files.pythonhosted.org/packages/d7/45/8a707f23c202208d7b286d78ad6233f50dcf929319b664b6cc18a03c1aae/coverage-7.6.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e78b270eadb5702938c3dbe9367f878249b5ef9a2fcc5360ac7bff694310d17b", size = 240072, upload-time = "2024-12-26T16:57:56.087Z" }, + { url = "https://files.pythonhosted.org/packages/66/02/603ce0ac2d02bc7b393279ef618940b4a0535b0868ee791140bda9ecfa40/coverage-7.6.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:714f942b9c15c3a7a5fe6876ce30af831c2ad4ce902410b7466b662358c852c0", size = 238386, upload-time = "2024-12-26T16:57:57.572Z" }, + { url = "https://files.pythonhosted.org/packages/04/62/4e6887e9be060f5d18f1dd58c2838b2d9646faf353232dec4e2d4b1c8644/coverage-7.6.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:abb02e2f5a3187b2ac4cd46b8ced85a0858230b577ccb2c62c81482ca7d18852", size = 240054, upload-time = "2024-12-26T16:57:58.967Z" }, + { url = "https://files.pythonhosted.org/packages/5c/74/83ae4151c170d8bd071924f212add22a0e62a7fe2b149edf016aeecad17c/coverage-7.6.10-cp312-cp312-win32.whl", hash = "sha256:55b201b97286cf61f5e76063f9e2a1d8d2972fc2fcfd2c1272530172fd28c359", size = 210904, upload-time = "2024-12-26T16:58:00.688Z" }, + { url = "https://files.pythonhosted.org/packages/c3/54/de0893186a221478f5880283119fc40483bc460b27c4c71d1b8bba3474b9/coverage-7.6.10-cp312-cp312-win_amd64.whl", hash = "sha256:e4ae5ac5e0d1e4edfc9b4b57b4cbecd5bc266a6915c500f358817a8496739247", size = 211692, upload-time = "2024-12-26T16:58:02.35Z" }, + { url = "https://files.pythonhosted.org/packages/25/6d/31883d78865529257bf847df5789e2ae80e99de8a460c3453dbfbe0db069/coverage-7.6.10-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:05fca8ba6a87aabdd2d30d0b6c838b50510b56cdcfc604d40760dae7153b73d9", size = 208308, upload-time = "2024-12-26T16:58:04.487Z" }, + { url = "https://files.pythonhosted.org/packages/70/22/3f2b129cc08de00c83b0ad6252e034320946abfc3e4235c009e57cfeee05/coverage-7.6.10-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9e80eba8801c386f72e0712a0453431259c45c3249f0009aff537a517b52942b", size = 208565, upload-time = "2024-12-26T16:58:06.774Z" }, + { url = "https://files.pythonhosted.org/packages/97/0a/d89bc2d1cc61d3a8dfe9e9d75217b2be85f6c73ebf1b9e3c2f4e797f4531/coverage-7.6.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a372c89c939d57abe09e08c0578c1d212e7a678135d53aa16eec4430adc5e690", size = 241083, upload-time = "2024-12-26T16:58:10.27Z" }, + { url = "https://files.pythonhosted.org/packages/4c/81/6d64b88a00c7a7aaed3a657b8eaa0931f37a6395fcef61e53ff742b49c97/coverage-7.6.10-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ec22b5e7fe7a0fa8509181c4aac1db48f3dd4d3a566131b313d1efc102892c18", size = 238235, upload-time = "2024-12-26T16:58:12.497Z" }, + { url = "https://files.pythonhosted.org/packages/9a/0b/7797d4193f5adb4b837207ed87fecf5fc38f7cc612b369a8e8e12d9fa114/coverage-7.6.10-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26bcf5c4df41cad1b19c84af71c22cbc9ea9a547fc973f1f2cc9a290002c8b3c", size = 240220, upload-time = "2024-12-26T16:58:15.619Z" }, + { url = "https://files.pythonhosted.org/packages/65/4d/6f83ca1bddcf8e51bf8ff71572f39a1c73c34cf50e752a952c34f24d0a60/coverage-7.6.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e4630c26b6084c9b3cb53b15bd488f30ceb50b73c35c5ad7871b869cb7365fd", size = 239847, upload-time = "2024-12-26T16:58:17.126Z" }, + { url = "https://files.pythonhosted.org/packages/30/9d/2470df6aa146aff4c65fee0f87f58d2164a67533c771c9cc12ffcdb865d5/coverage-7.6.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2396e8116db77789f819d2bc8a7e200232b7a282c66e0ae2d2cd84581a89757e", size = 237922, upload-time = "2024-12-26T16:58:20.198Z" }, + { url = "https://files.pythonhosted.org/packages/08/dd/723fef5d901e6a89f2507094db66c091449c8ba03272861eaefa773ad95c/coverage-7.6.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:79109c70cc0882e4d2d002fe69a24aa504dec0cc17169b3c7f41a1d341a73694", size = 239783, upload-time = "2024-12-26T16:58:23.614Z" }, + { url = "https://files.pythonhosted.org/packages/3d/f7/64d3298b2baf261cb35466000628706ce20a82d42faf9b771af447cd2b76/coverage-7.6.10-cp313-cp313-win32.whl", hash = "sha256:9e1747bab246d6ff2c4f28b4d186b205adced9f7bd9dc362051cc37c4a0c7bd6", size = 210965, upload-time = "2024-12-26T16:58:26.765Z" }, + { url = "https://files.pythonhosted.org/packages/d5/58/ec43499a7fc681212fe7742fe90b2bc361cdb72e3181ace1604247a5b24d/coverage-7.6.10-cp313-cp313-win_amd64.whl", hash = "sha256:254f1a3b1eef5f7ed23ef265eaa89c65c8c5b6b257327c149db1ca9d4a35f25e", size = 211719, upload-time = "2024-12-26T16:58:28.781Z" }, + { url = "https://files.pythonhosted.org/packages/ab/c9/f2857a135bcff4330c1e90e7d03446b036b2363d4ad37eb5e3a47bbac8a6/coverage-7.6.10-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2ccf240eb719789cedbb9fd1338055de2761088202a9a0b73032857e53f612fe", size = 209050, upload-time = "2024-12-26T16:58:31.616Z" }, + { url = "https://files.pythonhosted.org/packages/aa/b3/f840e5bd777d8433caa9e4a1eb20503495709f697341ac1a8ee6a3c906ad/coverage-7.6.10-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0c807ca74d5a5e64427c8805de15b9ca140bba13572d6d74e262f46f50b13273", size = 209321, upload-time = "2024-12-26T16:58:34.509Z" }, + { url = "https://files.pythonhosted.org/packages/85/7d/125a5362180fcc1c03d91850fc020f3831d5cda09319522bcfa6b2b70be7/coverage-7.6.10-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bcfa46d7709b5a7ffe089075799b902020b62e7ee56ebaed2f4bdac04c508d8", size = 252039, upload-time = "2024-12-26T16:58:36.072Z" }, + { url = "https://files.pythonhosted.org/packages/a9/9c/4358bf3c74baf1f9bddd2baf3756b54c07f2cfd2535f0a47f1e7757e54b3/coverage-7.6.10-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e0de1e902669dccbf80b0415fb6b43d27edca2fbd48c74da378923b05316098", size = 247758, upload-time = "2024-12-26T16:58:39.458Z" }, + { url = "https://files.pythonhosted.org/packages/cf/c7/de3eb6fc5263b26fab5cda3de7a0f80e317597a4bad4781859f72885f300/coverage-7.6.10-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f7b444c42bbc533aaae6b5a2166fd1a797cdb5eb58ee51a92bee1eb94a1e1cb", size = 250119, upload-time = "2024-12-26T16:58:41.018Z" }, + { url = "https://files.pythonhosted.org/packages/3e/e6/43de91f8ba2ec9140c6a4af1102141712949903dc732cf739167cfa7a3bc/coverage-7.6.10-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b330368cb99ef72fcd2dc3ed260adf67b31499584dc8a20225e85bfe6f6cfed0", size = 249597, upload-time = "2024-12-26T16:58:42.827Z" }, + { url = "https://files.pythonhosted.org/packages/08/40/61158b5499aa2adf9e37bc6d0117e8f6788625b283d51e7e0c53cf340530/coverage-7.6.10-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:9a7cfb50515f87f7ed30bc882f68812fd98bc2852957df69f3003d22a2aa0abf", size = 247473, upload-time = "2024-12-26T16:58:44.486Z" }, + { url = "https://files.pythonhosted.org/packages/50/69/b3f2416725621e9f112e74e8470793d5b5995f146f596f133678a633b77e/coverage-7.6.10-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f93531882a5f68c28090f901b1d135de61b56331bba82028489bc51bdd818d2", size = 248737, upload-time = "2024-12-26T16:58:45.919Z" }, + { url = "https://files.pythonhosted.org/packages/3c/6e/fe899fb937657db6df31cc3e61c6968cb56d36d7326361847440a430152e/coverage-7.6.10-cp313-cp313t-win32.whl", hash = "sha256:89d76815a26197c858f53c7f6a656686ec392b25991f9e409bcef020cd532312", size = 211611, upload-time = "2024-12-26T16:58:47.883Z" }, + { url = "https://files.pythonhosted.org/packages/1c/55/52f5e66142a9d7bc93a15192eba7a78513d2abf6b3558d77b4ca32f5f424/coverage-7.6.10-cp313-cp313t-win_amd64.whl", hash = "sha256:54a5f0f43950a36312155dae55c505a76cd7f2b12d26abeebbe7a0b36dbc868d", size = 212781, upload-time = "2024-12-26T16:58:50.822Z" }, +] + +[[package]] +name = "django" +version = "5.1.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "asgiref" }, + { name = "sqlparse" }, + { name = "tzdata", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5f/57/11186e493ddc5a5e92cc7924a6363f7d4c2b645f7d7cb04a26a63f9bfb8b/Django-5.1.7.tar.gz", hash = "sha256:30de4ee43a98e5d3da36a9002f287ff400b43ca51791920bfb35f6917bfe041c", size = 10716510, upload-time = "2025-03-06T12:52:18.938Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/0f/7e042df3d462d39ae01b27a09ee76653692442bc3701fbfa6cb38e12889d/Django-5.1.7-py3-none-any.whl", hash = "sha256:1323617cb624add820cb9611cdcc788312d250824f92ca6048fda8625514af2b", size = 8276912, upload-time = "2025-03-06T12:52:12.784Z" }, +] + +[[package]] +name = "django-cors-headers" +version = "4.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "asgiref" }, + { name = "django" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c6/e5/3b67fc05b9c02b926411436dfc553829bc00843706ce7f99752433017f47/django_cors_headers-4.6.0.tar.gz", hash = "sha256:14d76b4b4c8d39375baeddd89e4f08899051eeaf177cb02a29bd6eae8cf63aa8", size = 20961, upload-time = "2024-10-29T10:38:15.281Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/52/73/689532cf164ab10ed1521d825ea156656520cec98886c8d2ac1ce8829220/django_cors_headers-4.6.0-py3-none-any.whl", hash = "sha256:8edbc0497e611c24d5150e0055d3b178c6534b8ed826fb6f53b21c63f5d48ba3", size = 12791, upload-time = "2024-10-29T10:38:13.784Z" }, +] + +[[package]] +name = "django-filter" +version = "25.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "django" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b5/40/c702a6fe8cccac9bf426b55724ebdf57d10a132bae80a17691d0cf0b9bac/django_filter-25.1.tar.gz", hash = "sha256:1ec9eef48fa8da1c0ac9b411744b16c3f4c31176c867886e4c48da369c407153", size = 143021, upload-time = "2025-02-14T16:30:53.238Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/a6/70dcd68537c434ba7cb9277d403c5c829caf04f35baf5eb9458be251e382/django_filter-25.1-py3-none-any.whl", hash = "sha256:4fa48677cf5857b9b1347fed23e355ea792464e0fe07244d1fdfb8a806215b80", size = 94114, upload-time = "2025-02-14T16:30:50.435Z" }, +] + +[[package]] +name = "djangorestframework" +version = "3.15.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "django" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2c/ce/31482eb688bdb4e271027076199e1aa8d02507e530b6d272ab8b4481557c/djangorestframework-3.15.2.tar.gz", hash = "sha256:36fe88cd2d6c6bec23dca9804bab2ba5517a8bb9d8f47ebc68981b56840107ad", size = 1067420, upload-time = "2024-06-19T07:59:32.891Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/b6/fa99d8f05eff3a9310286ae84c4059b08c301ae4ab33ae32e46e8ef76491/djangorestframework-3.15.2-py3-none-any.whl", hash = "sha256:2b8871b062ba1aefc2de01f773875441a961fefbf79f5eed1e32b2f096944b20", size = 1071235, upload-time = "2024-06-19T07:59:26.106Z" }, +] + +[[package]] +name = "djangorestframework-simplejwt" +version = "5.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "django" }, + { name = "djangorestframework" }, + { name = "pyjwt" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8d/5f/1c130e823f734ba776c3925ad8e7c28ca1a59feb22d039b3810d1f8c0b34/djangorestframework_simplejwt-5.4.0.tar.gz", hash = "sha256:cccecce1a0e1a4a240fae80da73e5fc23055bababb8b67de88fa47cd36822320", size = 96648, upload-time = "2025-01-07T08:25:11.667Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f0/41/d6f67d24f46c7d8ee6dcb85c9abb94bad23140210bfb8c454641d278dfd2/djangorestframework_simplejwt-5.4.0-py3-none-any.whl", hash = "sha256:7aec953db9ed4163430c16d086eecb0f028f814ce6bba62b06c25919261e9077", size = 102316, upload-time = "2025-01-07T08:24:30.503Z" }, +] + +[[package]] +name = "ebooklib" +version = "0.20" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "lxml" }, + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/77/85/322e8882a582d4b707220d1929cfb74c125f2ba513991edbce40dbc462de/ebooklib-0.20.tar.gz", hash = "sha256:35e2f9d7d39907be8d39ae2deb261b19848945903ae3dbb6577b187ead69e985", size = 127066, upload-time = "2025-10-26T20:56:20.968Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bf/ee/aa015c5de8b0dc42a8e507eae8c2de5d1c0e068c896858fec6d502402ed6/ebooklib-0.20-py3-none-any.whl", hash = "sha256:fff5322517a37e31c972d27be7d982cc3928c16b3dcc5fd7e8f7c0f5d7bcf42b", size = 40995, upload-time = "2025-10-26T20:56:19.104Z" }, +] + +[[package]] +name = "gunicorn" +version = "23.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/34/72/9614c465dc206155d93eff0ca20d42e1e35afc533971379482de953521a4/gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec", size = 375031, upload-time = "2024-08-10T20:25:27.378Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/7d/6dac2a6e1eba33ee43f318edbed4ff29151a49b5d37f080aad1e6469bca4/gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d", size = 85029, upload-time = "2024-08-10T20:25:24.996Z" }, +] + +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, +] + +[[package]] +name = "httpcore" +version = "1.0.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, +] + +[[package]] +name = "idna" +version = "3.18" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/63/9496c57188a2ee585e0f1db071d75089a11e98aa86eb99d9d7618fc1edce/idna-3.18.tar.gz", hash = "sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848", size = 196711, upload-time = "2026-06-02T14:34:07.794Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/5e/d4e9f1a599fb8e573b7b87160658329fbf28d19eac2718f51fc3def3aa5a/idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2", size = 65455, upload-time = "2026-06-02T14:34:06.319Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, +] + +[[package]] +name = "lxml" +version = "6.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/3b/aab6728cae887456f409b4d75e8a01856e4f04bd510de38052a47768b680/lxml-6.1.1.tar.gz", hash = "sha256:ba96ae44888e0185281e937633a743ea90d5a196c6000f82565ebb0580012d40", size = 4197430, upload-time = "2026-05-18T19:19:06.424Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/6e/c4add832b6fc1e887125b96f880d7b9b70aae5248718e046b1704bcac4b9/lxml-6.1.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:104c09bda8d2a562824c0e319d0768ce26a779b7601e0931d33b09b53c392ef7", size = 8570821, upload-time = "2026-05-18T19:17:42.068Z" }, + { url = "https://files.pythonhosted.org/packages/22/00/ff3009c88e65de8011630acf8ab5a09cb2becd2aaf47fba2f3449f6224e9/lxml-6.1.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:25c6997a9a534e016695a0ba06b2f07945de682731ff01065b6d5a4474179da1", size = 4624252, upload-time = "2026-05-18T19:17:47.897Z" }, + { url = "https://files.pythonhosted.org/packages/42/95/bb63f0fd62e554fe078e1fb3c8fe9083c14ddc7ad7fa178d10e57e071ac7/lxml-6.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c921ba5c51e4e9f63b8b00267d06566e1f63407408a0496da2d1d0bfc819c7fc", size = 4930746, upload-time = "2026-05-18T19:18:29.637Z" }, + { url = "https://files.pythonhosted.org/packages/eb/99/0013e8d9b5960f4f041cf0b73e2f80c23eb5205b1f7bfb20203243651359/lxml-6.1.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:54a7f95e4de5fb94e2f9f4b9055c6ba33bf3d628fd77a1d647c5923caa2cdcdc", size = 5093723, upload-time = "2026-05-18T19:18:34.168Z" }, + { url = "https://files.pythonhosted.org/packages/29/91/317b332636bfc7bddcff828d41b3307f50043f4b237e40849c333d80fa1a/lxml-6.1.1-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f2ec43df44b1f76249ee0a615334f9b5b060e1c8bd90e706dad2d14d02f383", size = 5005557, upload-time = "2026-05-18T19:18:39.798Z" }, + { url = "https://files.pythonhosted.org/packages/42/2f/cc9bf06afe70f9c9093ae60855d9759da9db601ec4080f7473319666ffd7/lxml-6.1.1-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:70ef8a7e102a1508f8121aae5b0867abd663f72c14f0a9c937e6554cb4587b7b", size = 5631036, upload-time = "2026-05-18T19:18:44.858Z" }, + { url = "https://files.pythonhosted.org/packages/08/f6/af32e23e563971ffb0fb86be52bc5be5c2c118858ffc119bf6a9039b173d/lxml-6.1.1-cp312-cp312-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ebe6af670449830d6d9b752c256a983291c766a1365ba5d5460048f9e33a7818", size = 5240367, upload-time = "2026-05-18T19:18:49.217Z" }, + { url = "https://files.pythonhosted.org/packages/78/83/8555d40948b09ce86f1bd0c68a7ac31d07b1929f92cc1b074006c97ef2d2/lxml-6.1.1-cp312-cp312-manylinux_2_28_i686.whl", hash = "sha256:27acc820660aaffa4f7c087f29120e12980f7779d56d8492d263170111284740", size = 5350171, upload-time = "2026-05-18T19:18:52.779Z" }, + { url = "https://files.pythonhosted.org/packages/63/75/5d92da93729b7bad783689e6496049fa40927b45bec7bf183c981de3ca70/lxml-6.1.1-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:1db753c9115ec7100d073b744d17e25e88a8f90f5c39b2f5dd878149af59671f", size = 4694874, upload-time = "2026-05-18T19:18:55.139Z" }, + { url = "https://files.pythonhosted.org/packages/c5/b5/3aad415a9a25b822e783f15deeb4dffccf5113030f1afa2222dd929313d9/lxml-6.1.1-cp312-cp312-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c4f469aebd783bb741c2ecb2a681008fd26bfe5c16a9a72ed5467f834e810df2", size = 5244492, upload-time = "2026-05-18T19:19:01.28Z" }, + { url = "https://files.pythonhosted.org/packages/f1/a1/5fcf7eb9904b80086aa47dcf0027de07b1bb990afad2e6823144c368ae04/lxml-6.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:766b010012d59470072c1816b5b6c69f1d243e5db36ea5968e94accf430a4635", size = 5048232, upload-time = "2026-05-18T19:18:12.67Z" }, + { url = "https://files.pythonhosted.org/packages/77/74/1f601b63c7a69fcdf10fa9b148c81da8442204194f6c55509cc485c786b9/lxml-6.1.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b8d812c6011c08b8111a15e54dd990b8923692d80adf35488bee34026c35accf", size = 4777023, upload-time = "2026-05-18T19:18:15.928Z" }, + { url = "https://files.pythonhosted.org/packages/a2/b9/7a78f51aec95b1bf780d78e12705a9f6533284f8693dc5c0e6724fa53d3f/lxml-6.1.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:fe0306bd29505a9177aac19f1877174b0e7422c222a59f70b2cd41633448c3dc", size = 5645773, upload-time = "2026-05-18T19:18:23.223Z" }, + { url = "https://files.pythonhosted.org/packages/a5/6e/98a7b7ad54e4e74fa1f20fff776913980619d0ebe5558232d7da6580bdd8/lxml-6.1.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:5ba186ad207446c65d3bb3d3e0412b032b1d9f595e59861e2354798c5703d955", size = 5233088, upload-time = "2026-05-18T19:18:31.433Z" }, + { url = "https://files.pythonhosted.org/packages/65/d1/bc0ed2427bf609f2ee10da303a6a226f9c8bce94f945dc29a32ce55de6e4/lxml-6.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:aa366a1e55b8ebfe8ca8ddc3cfe75c8ebade181aeb0f661d0cb05986b647f72a", size = 5260995, upload-time = "2026-05-18T19:18:37.091Z" }, + { url = "https://files.pythonhosted.org/packages/69/8b/6772e1a4b513fc50a8d931f19edde0e13ae6918510a1e13ff67864f3e5ed/lxml-6.1.1-cp312-cp312-win32.whl", hash = "sha256:126c93f7f56f0eda92f6d8c619edc463a4f23d9252f1c9d0405a76f25fa9f11a", size = 3596382, upload-time = "2026-05-18T19:17:18.37Z" }, + { url = "https://files.pythonhosted.org/packages/1b/89/45198e9624762af2dfd2cb8782598477ceb29f6e59caab560388ae1f4ec1/lxml-6.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:26e6eda8d38c1fcab1090dd196ee87cbd13788e531937610e2589085de074e77", size = 3997255, upload-time = "2026-05-18T19:17:56.781Z" }, + { url = "https://files.pythonhosted.org/packages/90/a9/7a54b6834088d9ae528a7b780584ba6a39a9457b0ac330479f20ffbc9449/lxml-6.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:6540377fbd53fe1b629172288c464fb18db11ce1fa7dc15891da10aa9dcc3e7f", size = 3659610, upload-time = "2026-05-19T19:22:50.843Z" }, + { url = "https://files.pythonhosted.org/packages/a5/eb/7e6f37c5584ccbb2ff267f56fd0339016938c1c8684cfefab9b33ffc2f36/lxml-6.1.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:68a9198d0fc122d14bb76837de9aa80cf84caed990b5b237f532ed87d3706736", size = 8559780, upload-time = "2026-05-18T19:17:57.661Z" }, + { url = "https://files.pythonhosted.org/packages/a1/36/587c2521cf23a2cd6c9c22108aa7528f683a1f195ed7ccd23a4b1786ad36/lxml-6.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7d47866cb32fb503450b6edc9df355d10dc49836af2e89901bd6ac6b0896d9d9", size = 4618006, upload-time = "2026-05-18T19:18:04.452Z" }, + { url = "https://files.pythonhosted.org/packages/6e/ca/ab7bfe2bf4c972af5e7878262845ead3a24a929a9b04bc11c7c1ece6c82a/lxml-6.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:eb7c9811bfaa8b1ed5ed319f5d370dfbcaa59d52ea64be2a5a85e18195930354", size = 4924139, upload-time = "2026-05-18T19:19:04.873Z" }, + { url = "https://files.pythonhosted.org/packages/6b/55/a0c72851dfee5ecc689f949723a73dea457758912542cb955b108eaf0d8f/lxml-6.1.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:762ff394d5bd56da0cf034a23dcce4e13923f15321a2adfa2ac00201dc6d3fca", size = 5082329, upload-time = "2026-05-18T19:19:09.728Z" }, + { url = "https://files.pythonhosted.org/packages/f0/b6/0608f7d61a3b96cc67e5648a3d906e31a5082093e10e7be65b3886289938/lxml-6.1.1-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a088f287f7d8275a33c07f2cac6c50b9319309a0200a39e7e75d80c707723099", size = 4993564, upload-time = "2026-05-18T19:19:13.608Z" }, + { url = "https://files.pythonhosted.org/packages/4c/66/ae227524b066d29d55bf0b453d93d2d793c40218657d643dcbbca13b8faf/lxml-6.1.1-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e902da4b04e6b52e5893900d4b8ab46068f75f3561f01bf1080957f9fd932ed6", size = 5613467, upload-time = "2026-05-18T19:19:16.228Z" }, + { url = "https://files.pythonhosted.org/packages/a6/76/dbe4a00b50385e40194231dcfe5a12c059de7cf90e89c83407d2b085b719/lxml-6.1.1-cp313-cp313-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1d4962d4c66bf830a7e59ed6cfc17d148149898a3aefa8ec6e59763e6e3ed085", size = 5228304, upload-time = "2026-05-18T19:19:19.354Z" }, + { url = "https://files.pythonhosted.org/packages/1c/01/00b1b8442ed2041793336868ba0b9ea4b13d7da7c085c6404c207a63bf79/lxml-6.1.1-cp313-cp313-manylinux_2_28_i686.whl", hash = "sha256:581d4c8ae690a6609e64862dd6b7c2489635c2d13907fc2b20f2bc200ff1d21e", size = 5341607, upload-time = "2026-05-18T19:19:22.297Z" }, + { url = "https://files.pythonhosted.org/packages/63/36/1ad29931e9a4638bb707869f01d423a6c815f82152138d1a40dfcfde2b95/lxml-6.1.1-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:876e1ff5930ed8bf295ec5ef9a8155e9b6b1876bbf1deed8b3a8069311875a8f", size = 4700168, upload-time = "2026-05-18T19:19:25.133Z" }, + { url = "https://files.pythonhosted.org/packages/3c/d1/a9536cecf9be18a0dc72d32bead283a2332d1ffebd2dd3ac70ce444686e5/lxml-6.1.1-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9eb9b5a968f6e0f6d640092a567e14529ff8cea2e29d00da6f78a79fa49f013c", size = 5232487, upload-time = "2026-05-18T19:19:28.603Z" }, + { url = "https://files.pythonhosted.org/packages/0e/77/b4fb1e03bf5d130e879214d3100092e386418807fb74dd0adc4b0a48f351/lxml-6.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:aa49e06d94aba782c6a02eecb7e507969e7e7a41b267f1b359bb35585f295d5b", size = 5044231, upload-time = "2026-05-18T19:18:42.246Z" }, + { url = "https://files.pythonhosted.org/packages/26/4c/d00daeeb0a5530c4028a9232aa1b93db3ef4ed2158c116ea73c79a9765b3/lxml-6.1.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:70cdfd80589d59e43e18005dd7244e8895e93db8ab6a620b7e23df5445a4e3d2", size = 4769450, upload-time = "2026-05-18T19:18:48.013Z" }, + { url = "https://files.pythonhosted.org/packages/ed/6a/715a3a8d156ce42f29cf014706f5410c2ff3b02267774110fc23266409fe/lxml-6.1.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:aad9aa39483ed8ec44d6d2e59e5b98a0d80676ef0d92f44bfc374836111f62f5", size = 5635874, upload-time = "2026-05-18T19:18:51.914Z" }, + { url = "https://files.pythonhosted.org/packages/45/37/0544bc21dde2a88f3a17b504e6fc79c0e01d25a33c2f6079724e9e72b9c7/lxml-6.1.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:d49514be2f28d895c38cf9d2b72d7b9a07d00314519f456c0b50b53cfcf4c785", size = 5223987, upload-time = "2026-05-18T19:18:59.715Z" }, + { url = "https://files.pythonhosted.org/packages/4d/f8/f6a5e8185bcb28c2befae3d31f8e3df3b811cb0f47746517a81279fcafe1/lxml-6.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:47402e62c52ff5988c1e8c6c63177f5708bccf48e366dea4e3dcf1e645e04947", size = 5250276, upload-time = "2026-05-18T19:19:03.834Z" }, + { url = "https://files.pythonhosted.org/packages/c7/f2/1a2b9f1b7a49d45495369be7ef9ad05b262930f2eab3e3145706fca8083f/lxml-6.1.1-cp313-cp313-win32.whl", hash = "sha256:3483644525531e1d5762b0c44a8e18b6efba321b6dcf8a8952de10b037618bca", size = 3596903, upload-time = "2026-05-18T19:17:29.863Z" }, + { url = "https://files.pythonhosted.org/packages/e6/99/f4ffb024f238eec2131aaa09f3278fb6129cf892741bf68e1fc1afb8c100/lxml-6.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:a10bd2fd62e8ce916ececb342f348f190724a098c1faa056fdfb2a22ad5e8660", size = 3995869, upload-time = "2026-05-18T19:18:02.596Z" }, + { url = "https://files.pythonhosted.org/packages/d1/53/70eb8c5c6037f27448f1e3c54ebede9545a801ae63f0a7254afca4fe8e45/lxml-6.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:424aa57aca0897eb922aef34395bd1289b3b6f04e6bae20ea123c0c7e333cffc", size = 3658490, upload-time = "2026-05-19T19:22:53.846Z" }, + { url = "https://files.pythonhosted.org/packages/13/e2/2e325795566de01d0d7c3bb57d3c370616b2d07b01214e84eec5d3b10963/lxml-6.1.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:19b7ab10b210b0b3ad7985d9ac4eb66ab09a90b20fe6e2f7ba55d01a234345d0", size = 8577146, upload-time = "2026-05-18T19:18:17.765Z" }, + { url = "https://files.pythonhosted.org/packages/93/cf/5630b5e4be7d2e6bee8efe83865c925221103cf0221303b104ce134b01e2/lxml-6.1.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:c08e5c694306507275f2290073350c4f32e383db15213b2c69e7ff39c1193840", size = 4623866, upload-time = "2026-05-18T19:18:30.669Z" }, + { url = "https://files.pythonhosted.org/packages/d2/51/3904907c063451cf8d4a5c9fe0cad95fa1f4ec57f4e3884fa0731bd7a305/lxml-6.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:74a9717fd0d82effef5c2854f0d917231d5324b5a3eb7275c43ac9fa32f97a14", size = 4950022, upload-time = "2026-05-18T19:19:31.958Z" }, + { url = "https://files.pythonhosted.org/packages/94/cd/9c7611a51c37a2830928405817cc5d56a97f64fab83cc3f628748b135749/lxml-6.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:efe0374196335f93b53269acd811b944f2e6bdc88e8894f214bd636455484909", size = 5086695, upload-time = "2026-05-18T19:19:34.764Z" }, + { url = "https://files.pythonhosted.org/packages/da/d6/24e3b5906abb0b674ff2ae195bc3ce59708df2bcd17cf17703b2d7dd643a/lxml-6.1.1-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ac931cdc9442c1763b8a8f6cd62c0c938737eafc5be75eff88df55fc73bc0d00", size = 5031642, upload-time = "2026-05-18T19:19:37.771Z" }, + { url = "https://files.pythonhosted.org/packages/2d/db/6ec54f99019838bff54785c51da07f189eb4676861c5f2730962b0d8d665/lxml-6.1.1-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:aee395f5d0927f947758b4ec119fd5fc8ec71f07a1c5c52077b30b04c0fa6955", size = 5647338, upload-time = "2026-05-18T19:19:40.553Z" }, + { url = "https://files.pythonhosted.org/packages/42/3d/ef4dcfffd22d27a61805d8ed9f7fb888495bc6aa88648fa07c1eaa5586b6/lxml-6.1.1-cp314-cp314-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9395002973c827b3ed67db77e6ec09f092919a587022174554096a269378fb13", size = 5239528, upload-time = "2026-05-18T19:19:43.657Z" }, + { url = "https://files.pythonhosted.org/packages/62/bb/37fb3f0dff146bdcfa78eec47879273820b2a0bf350ec236ce14bd0b1c26/lxml-6.1.1-cp314-cp314-manylinux_2_28_i686.whl", hash = "sha256:73bc2086f141224ebddb7fc5c6a36ca58b31b94b561e1dfe8e073e3270fad1e7", size = 5350730, upload-time = "2026-05-18T19:19:46.307Z" }, + { url = "https://files.pythonhosted.org/packages/90/42/43253f168388df4fae1f38c01df36ddb9bee39e2048167b54cdcbae85ea3/lxml-6.1.1-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:3779def59032b81e44a5f70096ef6bf2082f8d901937dca354474ba09782e245", size = 4697530, upload-time = "2026-05-18T19:19:49.889Z" }, + { url = "https://files.pythonhosted.org/packages/eb/a8/c5a8504f81bbdfc8e7094c2c850cdb4ed6777fc4d5ddd9e5ab819f3b0d54/lxml-6.1.1-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:86c89b9d55ebf820ad7c90bc533410f0d098054f293351f10603c0c46ff598f5", size = 5250670, upload-time = "2026-05-18T19:19:53.199Z" }, + { url = "https://files.pythonhosted.org/packages/77/b7/c7e76ab18744d75e21f320ebf9ff9d1ceae2b54dd431ea5a64caf26c9672/lxml-6.1.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:19607c6bbff2a44cf3fe8250abccd20942d3462473e0a721d01d379ed017e462", size = 5084485, upload-time = "2026-05-18T19:19:08.422Z" }, + { url = "https://files.pythonhosted.org/packages/31/31/b35c53f8ef7b7c31cacd23d3638652fff7bcd1deb6eedb709ab43b685908/lxml-6.1.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:c6ed5141a5c7507cf3ee76bd363b0d6f801e3321adc35b5d825a23115faa5465", size = 4737635, upload-time = "2026-05-18T19:19:12.321Z" }, + { url = "https://files.pythonhosted.org/packages/d9/06/31f23c813a7fe8e0cb1b175e915b08c9bf4e86d225b210feadbdbe519667/lxml-6.1.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:62aeb7e85b5d60320b9d77eef2e773994e2c0ce10121b277e0a19804e1654a5a", size = 5670681, upload-time = "2026-05-18T19:19:15.001Z" }, + { url = "https://files.pythonhosted.org/packages/1a/bc/ce619bccc89b1fd9ad8a8e1330ee3f3beff9f2ff95b712d7bbcdd6e22fc3/lxml-6.1.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:b1b963fd8f5caa68e99dfae060d54de1fe9cba899b8718b44a00cdca53c3e590", size = 5238229, upload-time = "2026-05-18T19:19:18.131Z" }, + { url = "https://files.pythonhosted.org/packages/2f/5d/b329acbbedc0b619ebc2be6cf7ee9ed07e80892c88d4dfd612c33805789a/lxml-6.1.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:63876be28efefa04a1df615b46770e82042cce445cfdce55160522f57b231ccb", size = 5264191, upload-time = "2026-05-18T19:19:21.118Z" }, + { url = "https://files.pythonhosted.org/packages/d6/85/be36fb1425b30db3c3f9df75fe86343ebffb79e6320bd7f588e25bfeac39/lxml-6.1.1-cp314-cp314-win32.whl", hash = "sha256:7f7a92e8583f06b1fd49d01158143b8461cfcd135dcb10ec807270a3051bd603", size = 3657202, upload-time = "2026-05-18T19:17:39.509Z" }, + { url = "https://files.pythonhosted.org/packages/b8/ce/3cf9a827342269f54d405a6202397de63f07c69cbd6ce7d183a3f0cba1e9/lxml-6.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:b2d444f2e66624d68e9c6b211e28a76e22fff5fcabcfff4deac18b529b7d4137", size = 4064497, upload-time = "2026-05-18T19:18:14.662Z" }, + { url = "https://files.pythonhosted.org/packages/d9/3e/1a957bde8f0760039e627f94699f82caa782c9d838d86c3d28245ee67212/lxml-6.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:3fd9728a2735fda14f4e8235830c86b539e9661e849665bf926d3f867943b4bf", size = 3741991, upload-time = "2026-05-19T19:22:59.111Z" }, + { url = "https://files.pythonhosted.org/packages/78/b2/00ed55b3a2efa4658fb795c38d1090ec9b3e8a6c3683d4441fa517f09c3b/lxml-6.1.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:787b2496d0dbe8cd180984e8d29e3a6f76e7ea34db781cb3bd55e4ba1ef8b4ee", size = 8827545, upload-time = "2026-05-18T19:18:41.193Z" }, + { url = "https://files.pythonhosted.org/packages/c0/73/74573db19baa618d5f266f2407898b087ff6927115b00b71e5fc1b700847/lxml-6.1.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:2c8daa471358dc2d6fcf02165e80ec68f77871a286df95bc5cc3816153b0fd2c", size = 4735736, upload-time = "2026-05-18T19:18:46.761Z" }, + { url = "https://files.pythonhosted.org/packages/16/02/6f7061f4f95f51e545d48e87647c54791d204a4e881be4156e7a26ba5338/lxml-6.1.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:acd7d70b64c0aae0c7922cca83d288a16f5f6da523637697872253415269baef", size = 4970291, upload-time = "2026-05-18T19:19:56.215Z" }, + { url = "https://files.pythonhosted.org/packages/b0/02/55fc057d8283427dea7d6edb102e7a840239c77a64a983d92f62a304c0e9/lxml-6.1.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4f0dd2f01f9f8a89f565d000e03abcf0a13d692a346c8d22f628d49af098777a", size = 5102822, upload-time = "2026-05-18T19:19:59.223Z" }, + { url = "https://files.pythonhosted.org/packages/e4/48/8e1cf78d89d66850121d9255a2a24414c98f775da93b90cf976956c24b14/lxml-6.1.1-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b7e8a14c8634bf6f7a568634cb395305a6d964aeb5b7ee32248094bed3a7e2c", size = 5027923, upload-time = "2026-05-18T19:20:01.549Z" }, + { url = "https://files.pythonhosted.org/packages/ed/00/0632a0647612c8af24d26997b3b961397daa9d5b2581444805933629a4cb/lxml-6.1.1-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:86281fbdd6a8162756f8d603f37e3435bfa38043adb79c6dc6a2dfee065e7525", size = 5595843, upload-time = "2026-05-18T19:20:03.93Z" }, + { url = "https://files.pythonhosted.org/packages/bc/86/ab008a7dc360711b66858d61c80a5979a70a09f2aa2b05d9698df80b803d/lxml-6.1.1-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5d7152ec39ca7c402d8fb9bad86140a15b9503bd0c54484e3f1bbe3dd37ceca", size = 5224515, upload-time = "2026-05-18T19:20:06.381Z" }, + { url = "https://files.pythonhosted.org/packages/75/c6/2702ff375e728e34f56d9a45339a9cf7e4427e917f542225242d63a05afa/lxml-6.1.1-cp314-cp314t-manylinux_2_28_i686.whl", hash = "sha256:88d8cb75b9d82858497a5393e3c63cfbf03035225e4b35a49ed7ccb151e4dc0e", size = 5312511, upload-time = "2026-05-18T19:20:09.308Z" }, + { url = "https://files.pythonhosted.org/packages/b7/57/a5807c98f87a86f10ef9ffab35516df7c0f0c4b6d5d33e9f608ab9c04a31/lxml-6.1.1-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:f64ec5397ea6a41fc1b4af0380d79b44a755b5531dcaccd9940fb260dca93038", size = 4639206, upload-time = "2026-05-18T19:20:11.704Z" }, + { url = "https://files.pythonhosted.org/packages/1f/e1/8a0a2c35734812395f4da4eaf33748a7e5705bfb2a58b128da764339d5ec/lxml-6.1.1-cp314-cp314t-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d34bbf07dbc7ca5970671b1512e928991fb5e9d95365636c9b2d8b4f53af405e", size = 5232404, upload-time = "2026-05-18T19:20:14.064Z" }, + { url = "https://files.pythonhosted.org/packages/c2/e2/0e6a4dd5ad84d01d99aa7bae7cfefd4a760a0e0f8176818241de17d9b6c0/lxml-6.1.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:17e0e18d4ad8adbd0399291bc44845b69d9dd68439a3cdebdf35ff902ec05072", size = 5083769, upload-time = "2026-05-18T19:19:23.758Z" }, + { url = "https://files.pythonhosted.org/packages/a0/7e/161f33d463f6ffc1c7679104b65086dea120080d49dde4d238f015aaee2f/lxml-6.1.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:3ab541146f1f6968c462d6c2ac495148e8cdba2f8347700b2141b6ec5a75bf52", size = 4758936, upload-time = "2026-05-18T19:19:27.256Z" }, + { url = "https://files.pythonhosted.org/packages/f1/fb/2369825e3f6ca99305bf9f7b7085fda91c8b0922a89e54d900974aa3ef85/lxml-6.1.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:2a0217714657e023ef4293500f65aa20fce6164c8fd6b08fa5bd4a859fb14b9b", size = 5620296, upload-time = "2026-05-18T19:19:29.993Z" }, + { url = "https://files.pythonhosted.org/packages/30/90/d61e383146f74c5ab683947ea14dc7b82778838ab9b95ea73a23b60d0191/lxml-6.1.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:05a82eb6e1530a64f26225b55cbd178113bd0b5af1c2b625f25e5296742c26d2", size = 5228598, upload-time = "2026-05-18T19:19:33.523Z" }, + { url = "https://files.pythonhosted.org/packages/76/2d/2dafd8149e94b05bb070690efd5bb2680720681e03ff03fc57d2b70a1105/lxml-6.1.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9e36f163528fc50cbef305f02a5fd66d404edf7049cdaff211dbc2cba5a7013e", size = 5247845, upload-time = "2026-05-18T19:19:36.649Z" }, + { url = "https://files.pythonhosted.org/packages/ce/68/b30e913340c380ddac9580c6e6230991fc37240ec4f64704833e4f3e2769/lxml-6.1.1-cp314-cp314t-win32.whl", hash = "sha256:649dda677cf3bd6ac9ae14007ba0c824ded8ce5808b53fc7431d9140399118c1", size = 3897345, upload-time = "2026-05-18T19:17:33.562Z" }, + { url = "https://files.pythonhosted.org/packages/3c/4e/9eb2af5335545f9fbcd7af57bcf87c6025d31eaa31b14ec184a6c8675328/lxml-6.1.1-cp314-cp314t-win_amd64.whl", hash = "sha256:793033d6c5cdf33a573f910d9bea14ef8f5771820411d118da8e1182edb53d5e", size = 4393350, upload-time = "2026-05-18T19:18:10.076Z" }, + { url = "https://files.pythonhosted.org/packages/7f/2c/0f1e93c636720e8a3eb59af2bfda99d98b55891e1c53bc30c2e0e865f01b/lxml-6.1.1-cp314-cp314t-win_arm64.whl", hash = "sha256:58bb955caba94e467d2a96da17660d2d704e0675894cba21ab8a775b8621fd1c", size = 3817223, upload-time = "2026-05-19T19:22:56.823Z" }, +] + +[[package]] +name = "packaging" +version = "26.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, +] + +[[package]] +name = "pillow" +version = "12.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/21/c2bcdd5906101a30244eaffc1b6e6ce71a31bd0742a01eb89e660ebfac2d/pillow-12.2.0.tar.gz", hash = "sha256:a830b1a40919539d07806aa58e1b114df53ddd43213d9c8b75847eee6c0182b5", size = 46987819, upload-time = "2026-04-01T14:46:17.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/58/be/7482c8a5ebebbc6470b3eb791812fff7d5e0216c2be3827b30b8bb6603ed/pillow-12.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2d192a155bbcec180f8564f693e6fd9bccff5a7af9b32e2e4bf8c9c69dbad6b5", size = 5308279, upload-time = "2026-04-01T14:43:13.246Z" }, + { url = "https://files.pythonhosted.org/packages/d8/95/0a351b9289c2b5cbde0bacd4a83ebc44023e835490a727b2a3bd60ddc0f4/pillow-12.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f3f40b3c5a968281fd507d519e444c35f0ff171237f4fdde090dd60699458421", size = 4695490, upload-time = "2026-04-01T14:43:15.584Z" }, + { url = "https://files.pythonhosted.org/packages/de/af/4e8e6869cbed569d43c416fad3dc4ecb944cb5d9492defaed89ddd6fe871/pillow-12.2.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:03e7e372d5240cc23e9f07deca4d775c0817bffc641b01e9c3af208dbd300987", size = 6284462, upload-time = "2026-04-01T14:43:18.268Z" }, + { url = "https://files.pythonhosted.org/packages/e9/9e/c05e19657fd57841e476be1ab46c4d501bffbadbafdc31a6d665f8b737b6/pillow-12.2.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b86024e52a1b269467a802258c25521e6d742349d760728092e1bc2d135b4d76", size = 8094744, upload-time = "2026-04-01T14:43:20.716Z" }, + { url = "https://files.pythonhosted.org/packages/2b/54/1789c455ed10176066b6e7e6da1b01e50e36f94ba584dc68d9eebfe9156d/pillow-12.2.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7371b48c4fa448d20d2714c9a1f775a81155050d383333e0a6c15b1123dda005", size = 6398371, upload-time = "2026-04-01T14:43:23.443Z" }, + { url = "https://files.pythonhosted.org/packages/43/e3/fdc657359e919462369869f1c9f0e973f353f9a9ee295a39b1fea8ee1a77/pillow-12.2.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62f5409336adb0663b7caa0da5c7d9e7bdbaae9ce761d34669420c2a801b2780", size = 7087215, upload-time = "2026-04-01T14:43:26.758Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f8/2f6825e441d5b1959d2ca5adec984210f1ec086435b0ed5f52c19b3b8a6e/pillow-12.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:01afa7cf67f74f09523699b4e88c73fb55c13346d212a59a2db1f86b0a63e8c5", size = 6509783, upload-time = "2026-04-01T14:43:29.56Z" }, + { url = "https://files.pythonhosted.org/packages/67/f9/029a27095ad20f854f9dba026b3ea6428548316e057e6fc3545409e86651/pillow-12.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc3d34d4a8fbec3e88a79b92e5465e0f9b842b628675850d860b8bd300b159f5", size = 7212112, upload-time = "2026-04-01T14:43:32.091Z" }, + { url = "https://files.pythonhosted.org/packages/be/42/025cfe05d1be22dbfdb4f264fe9de1ccda83f66e4fc3aac94748e784af04/pillow-12.2.0-cp312-cp312-win32.whl", hash = "sha256:58f62cc0f00fd29e64b29f4fd923ffdb3859c9f9e6105bfc37ba1d08994e8940", size = 6378489, upload-time = "2026-04-01T14:43:34.601Z" }, + { url = "https://files.pythonhosted.org/packages/5d/7b/25a221d2c761c6a8ae21bfa3874988ff2583e19cf8a27bf2fee358df7942/pillow-12.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:7f84204dee22a783350679a0333981df803dac21a0190d706a50475e361c93f5", size = 7084129, upload-time = "2026-04-01T14:43:37.213Z" }, + { url = "https://files.pythonhosted.org/packages/10/e1/542a474affab20fd4a0f1836cb234e8493519da6b76899e30bcc5d990b8b/pillow-12.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:af73337013e0b3b46f175e79492d96845b16126ddf79c438d7ea7ff27783a414", size = 2463612, upload-time = "2026-04-01T14:43:39.421Z" }, + { url = "https://files.pythonhosted.org/packages/4a/01/53d10cf0dbad820a8db274d259a37ba50b88b24768ddccec07355382d5ad/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:8297651f5b5679c19968abefd6bb84d95fe30ef712eb1b2d9b2d31ca61267f4c", size = 4100837, upload-time = "2026-04-01T14:43:41.506Z" }, + { url = "https://files.pythonhosted.org/packages/0f/98/f3a6657ecb698c937f6c76ee564882945f29b79bad496abcba0e84659ec5/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:50d8520da2a6ce0af445fa6d648c4273c3eeefbc32d7ce049f22e8b5c3daecc2", size = 4176528, upload-time = "2026-04-01T14:43:43.773Z" }, + { url = "https://files.pythonhosted.org/packages/69/bc/8986948f05e3ea490b8442ea1c1d4d990b24a7e43d8a51b2c7d8b1dced36/pillow-12.2.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:766cef22385fa1091258ad7e6216792b156dc16d8d3fa607e7545b2b72061f1c", size = 3640401, upload-time = "2026-04-01T14:43:45.87Z" }, + { url = "https://files.pythonhosted.org/packages/34/46/6c717baadcd62bc8ed51d238d521ab651eaa74838291bda1f86fe1f864c9/pillow-12.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5d2fd0fa6b5d9d1de415060363433f28da8b1526c1c129020435e186794b3795", size = 5308094, upload-time = "2026-04-01T14:43:48.438Z" }, + { url = "https://files.pythonhosted.org/packages/71/43/905a14a8b17fdb1ccb58d282454490662d2cb89a6bfec26af6d3520da5ec/pillow-12.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56b25336f502b6ed02e889f4ece894a72612fe885889a6e8c4c80239ff6e5f5f", size = 4695402, upload-time = "2026-04-01T14:43:51.292Z" }, + { url = "https://files.pythonhosted.org/packages/73/dd/42107efcb777b16fa0393317eac58f5b5cf30e8392e266e76e51cff28c3d/pillow-12.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f1c943e96e85df3d3478f7b691f229887e143f81fedab9b20205349ab04d73ed", size = 6280005, upload-time = "2026-04-01T14:43:54.242Z" }, + { url = "https://files.pythonhosted.org/packages/a8/68/b93e09e5e8549019e61acf49f65b1a8530765a7f812c77a7461bca7e4494/pillow-12.2.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:03f6fab9219220f041c74aeaa2939ff0062bd5c364ba9ce037197f4c6d498cd9", size = 8090669, upload-time = "2026-04-01T14:43:57.335Z" }, + { url = "https://files.pythonhosted.org/packages/4b/6e/3ccb54ce8ec4ddd1accd2d89004308b7b0b21c4ac3d20fa70af4760a4330/pillow-12.2.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cdfebd752ec52bf5bb4e35d9c64b40826bc5b40a13df7c3cda20a2c03a0f5ed", size = 6395194, upload-time = "2026-04-01T14:43:59.864Z" }, + { url = "https://files.pythonhosted.org/packages/67/ee/21d4e8536afd1a328f01b359b4d3997b291ffd35a237c877b331c1c3b71c/pillow-12.2.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eedf4b74eda2b5a4b2b2fb4c006d6295df3bf29e459e198c90ea48e130dc75c3", size = 7082423, upload-time = "2026-04-01T14:44:02.74Z" }, + { url = "https://files.pythonhosted.org/packages/78/5f/e9f86ab0146464e8c133fe85df987ed9e77e08b29d8d35f9f9f4d6f917ba/pillow-12.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:00a2865911330191c0b818c59103b58a5e697cae67042366970a6b6f1b20b7f9", size = 6505667, upload-time = "2026-04-01T14:44:05.381Z" }, + { url = "https://files.pythonhosted.org/packages/ed/1e/409007f56a2fdce61584fd3acbc2bbc259857d555196cedcadc68c015c82/pillow-12.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1e1757442ed87f4912397c6d35a0db6a7b52592156014706f17658ff58bbf795", size = 7208580, upload-time = "2026-04-01T14:44:08.39Z" }, + { url = "https://files.pythonhosted.org/packages/23/c4/7349421080b12fb35414607b8871e9534546c128a11965fd4a7002ccfbee/pillow-12.2.0-cp313-cp313-win32.whl", hash = "sha256:144748b3af2d1b358d41286056d0003f47cb339b8c43a9ea42f5fea4d8c66b6e", size = 6375896, upload-time = "2026-04-01T14:44:11.197Z" }, + { url = "https://files.pythonhosted.org/packages/3f/82/8a3739a5e470b3c6cbb1d21d315800d8e16bff503d1f16b03a4ec3212786/pillow-12.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:390ede346628ccc626e5730107cde16c42d3836b89662a115a921f28440e6a3b", size = 7081266, upload-time = "2026-04-01T14:44:13.947Z" }, + { url = "https://files.pythonhosted.org/packages/c3/25/f968f618a062574294592f668218f8af564830ccebdd1fa6200f598e65c5/pillow-12.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:8023abc91fba39036dbce14a7d6535632f99c0b857807cbbbf21ecc9f4717f06", size = 2463508, upload-time = "2026-04-01T14:44:16.312Z" }, + { url = "https://files.pythonhosted.org/packages/4d/a4/b342930964e3cb4dce5038ae34b0eab4653334995336cd486c5a8c25a00c/pillow-12.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:042db20a421b9bafecc4b84a8b6e444686bd9d836c7fd24542db3e7df7baad9b", size = 5309927, upload-time = "2026-04-01T14:44:18.89Z" }, + { url = "https://files.pythonhosted.org/packages/9f/de/23198e0a65a9cf06123f5435a5d95cea62a635697f8f03d134d3f3a96151/pillow-12.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:dd025009355c926a84a612fecf58bb315a3f6814b17ead51a8e48d3823d9087f", size = 4698624, upload-time = "2026-04-01T14:44:21.115Z" }, + { url = "https://files.pythonhosted.org/packages/01/a6/1265e977f17d93ea37aa28aa81bad4fa597933879fac2520d24e021c8da3/pillow-12.2.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:88ddbc66737e277852913bd1e07c150cc7bb124539f94c4e2df5344494e0a612", size = 6321252, upload-time = "2026-04-01T14:44:23.663Z" }, + { url = "https://files.pythonhosted.org/packages/3c/83/5982eb4a285967baa70340320be9f88e57665a387e3a53a7f0db8231a0cd/pillow-12.2.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d362d1878f00c142b7e1a16e6e5e780f02be8195123f164edf7eddd911eefe7c", size = 8126550, upload-time = "2026-04-01T14:44:26.772Z" }, + { url = "https://files.pythonhosted.org/packages/4e/48/6ffc514adce69f6050d0753b1a18fd920fce8cac87620d5a31231b04bfc5/pillow-12.2.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c727a6d53cb0018aadd8018c2b938376af27914a68a492f59dfcaca650d5eea", size = 6433114, upload-time = "2026-04-01T14:44:29.615Z" }, + { url = "https://files.pythonhosted.org/packages/36/a3/f9a77144231fb8d40ee27107b4463e205fa4677e2ca2548e14da5cf18dce/pillow-12.2.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:efd8c21c98c5cc60653bcb311bef2ce0401642b7ce9d09e03a7da87c878289d4", size = 7115667, upload-time = "2026-04-01T14:44:32.773Z" }, + { url = "https://files.pythonhosted.org/packages/c1/fc/ac4ee3041e7d5a565e1c4fd72a113f03b6394cc72ab7089d27608f8aaccb/pillow-12.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9f08483a632889536b8139663db60f6724bfcb443c96f1b18855860d7d5c0fd4", size = 6538966, upload-time = "2026-04-01T14:44:35.252Z" }, + { url = "https://files.pythonhosted.org/packages/c0/a8/27fb307055087f3668f6d0a8ccb636e7431d56ed0750e07a60547b1e083e/pillow-12.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dac8d77255a37e81a2efcbd1fc05f1c15ee82200e6c240d7e127e25e365c39ea", size = 7238241, upload-time = "2026-04-01T14:44:37.875Z" }, + { url = "https://files.pythonhosted.org/packages/ad/4b/926ab182c07fccae9fcb120043464e1ff1564775ec8864f21a0ebce6ac25/pillow-12.2.0-cp313-cp313t-win32.whl", hash = "sha256:ee3120ae9dff32f121610bb08e4313be87e03efeadfc6c0d18f89127e24d0c24", size = 6379592, upload-time = "2026-04-01T14:44:40.336Z" }, + { url = "https://files.pythonhosted.org/packages/c2/c4/f9e476451a098181b30050cc4c9a3556b64c02cf6497ea421ac047e89e4b/pillow-12.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:325ca0528c6788d2a6c3d40e3568639398137346c3d6e66bb61db96b96511c98", size = 7085542, upload-time = "2026-04-01T14:44:43.251Z" }, + { url = "https://files.pythonhosted.org/packages/00/a4/285f12aeacbe2d6dc36c407dfbbe9e96d4a80b0fb710a337f6d2ad978c75/pillow-12.2.0-cp313-cp313t-win_arm64.whl", hash = "sha256:2e5a76d03a6c6dcef67edabda7a52494afa4035021a79c8558e14af25313d453", size = 2465765, upload-time = "2026-04-01T14:44:45.996Z" }, + { url = "https://files.pythonhosted.org/packages/bf/98/4595daa2365416a86cb0d495248a393dfc84e96d62ad080c8546256cb9c0/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:3adc9215e8be0448ed6e814966ecf3d9952f0ea40eb14e89a102b87f450660d8", size = 4100848, upload-time = "2026-04-01T14:44:48.48Z" }, + { url = "https://files.pythonhosted.org/packages/0b/79/40184d464cf89f6663e18dfcf7ca21aae2491fff1a16127681bf1fa9b8cf/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:6a9adfc6d24b10f89588096364cc726174118c62130c817c2837c60cf08a392b", size = 4176515, upload-time = "2026-04-01T14:44:51.353Z" }, + { url = "https://files.pythonhosted.org/packages/b0/63/703f86fd4c422a9cf722833670f4f71418fb116b2853ff7da722ea43f184/pillow-12.2.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:6a6e67ea2e6feda684ed370f9a1c52e7a243631c025ba42149a2cc5934dec295", size = 3640159, upload-time = "2026-04-01T14:44:53.588Z" }, + { url = "https://files.pythonhosted.org/packages/71/e0/fb22f797187d0be2270f83500aab851536101b254bfa1eae10795709d283/pillow-12.2.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2bb4a8d594eacdfc59d9e5ad972aa8afdd48d584ffd5f13a937a664c3e7db0ed", size = 5312185, upload-time = "2026-04-01T14:44:56.039Z" }, + { url = "https://files.pythonhosted.org/packages/ba/8c/1a9e46228571de18f8e28f16fabdfc20212a5d019f3e3303452b3f0a580d/pillow-12.2.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:80b2da48193b2f33ed0c32c38140f9d3186583ce7d516526d462645fd98660ae", size = 4695386, upload-time = "2026-04-01T14:44:58.663Z" }, + { url = "https://files.pythonhosted.org/packages/70/62/98f6b7f0c88b9addd0e87c217ded307b36be024d4ff8869a812b241d1345/pillow-12.2.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22db17c68434de69d8ecfc2fe821569195c0c373b25cccb9cbdacf2c6e53c601", size = 6280384, upload-time = "2026-04-01T14:45:01.5Z" }, + { url = "https://files.pythonhosted.org/packages/5e/03/688747d2e91cfbe0e64f316cd2e8005698f76ada3130d0194664174fa5de/pillow-12.2.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7b14cc0106cd9aecda615dd6903840a058b4700fcb817687d0ee4fc8b6e389be", size = 8091599, upload-time = "2026-04-01T14:45:04.5Z" }, + { url = "https://files.pythonhosted.org/packages/f6/35/577e22b936fcdd66537329b33af0b4ccfefaeabd8aec04b266528cddb33c/pillow-12.2.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cbeb542b2ebc6fcdacabf8aca8c1a97c9b3ad3927d46b8723f9d4f033288a0f", size = 6396021, upload-time = "2026-04-01T14:45:07.117Z" }, + { url = "https://files.pythonhosted.org/packages/11/8d/d2532ad2a603ca2b93ad9f5135732124e57811d0168155852f37fbce2458/pillow-12.2.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4bfd07bc812fbd20395212969e41931001fd59eb55a60658b0e5710872e95286", size = 7083360, upload-time = "2026-04-01T14:45:09.763Z" }, + { url = "https://files.pythonhosted.org/packages/5e/26/d325f9f56c7e039034897e7380e9cc202b1e368bfd04d4cbe6a441f02885/pillow-12.2.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9aba9a17b623ef750a4d11b742cbafffeb48a869821252b30ee21b5e91392c50", size = 6507628, upload-time = "2026-04-01T14:45:12.378Z" }, + { url = "https://files.pythonhosted.org/packages/5f/f7/769d5632ffb0988f1c5e7660b3e731e30f7f8ec4318e94d0a5d674eb65a4/pillow-12.2.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:deede7c263feb25dba4e82ea23058a235dcc2fe1f6021025dc71f2b618e26104", size = 7209321, upload-time = "2026-04-01T14:45:15.122Z" }, + { url = "https://files.pythonhosted.org/packages/6a/7a/c253e3c645cd47f1aceea6a8bacdba9991bf45bb7dfe927f7c893e89c93c/pillow-12.2.0-cp314-cp314-win32.whl", hash = "sha256:632ff19b2778e43162304d50da0181ce24ac5bb8180122cbe1bf4673428328c7", size = 6479723, upload-time = "2026-04-01T14:45:17.797Z" }, + { url = "https://files.pythonhosted.org/packages/cd/8b/601e6566b957ca50e28725cb6c355c59c2c8609751efbecd980db44e0349/pillow-12.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:4e6c62e9d237e9b65fac06857d511e90d8461a32adcc1b9065ea0c0fa3a28150", size = 7217400, upload-time = "2026-04-01T14:45:20.529Z" }, + { url = "https://files.pythonhosted.org/packages/d6/94/220e46c73065c3e2951bb91c11a1fb636c8c9ad427ac3ce7d7f3359b9b2f/pillow-12.2.0-cp314-cp314-win_arm64.whl", hash = "sha256:b1c1fbd8a5a1af3412a0810d060a78b5136ec0836c8a4ef9aa11807f2a22f4e1", size = 2554835, upload-time = "2026-04-01T14:45:23.162Z" }, + { url = "https://files.pythonhosted.org/packages/b6/ab/1b426a3974cb0e7da5c29ccff4807871d48110933a57207b5a676cccc155/pillow-12.2.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:57850958fe9c751670e49b2cecf6294acc99e562531f4bd317fa5ddee2068463", size = 5314225, upload-time = "2026-04-01T14:45:25.637Z" }, + { url = "https://files.pythonhosted.org/packages/19/1e/dce46f371be2438eecfee2a1960ee2a243bbe5e961890146d2dee1ff0f12/pillow-12.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d5d38f1411c0ed9f97bcb49b7bd59b6b7c314e0e27420e34d99d844b9ce3b6f3", size = 4698541, upload-time = "2026-04-01T14:45:28.355Z" }, + { url = "https://files.pythonhosted.org/packages/55/c3/7fbecf70adb3a0c33b77a300dc52e424dc22ad8cdc06557a2e49523b703d/pillow-12.2.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5c0a9f29ca8e79f09de89293f82fc9b0270bb4af1d58bc98f540cc4aedf03166", size = 6322251, upload-time = "2026-04-01T14:45:30.924Z" }, + { url = "https://files.pythonhosted.org/packages/1c/3c/7fbc17cfb7e4fe0ef1642e0abc17fc6c94c9f7a16be41498e12e2ba60408/pillow-12.2.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1610dd6c61621ae1cf811bef44d77e149ce3f7b95afe66a4512f8c59f25d9ebe", size = 8127807, upload-time = "2026-04-01T14:45:33.908Z" }, + { url = "https://files.pythonhosted.org/packages/ff/c3/a8ae14d6defd2e448493ff512fae903b1e9bd40b72efb6ec55ce0048c8ce/pillow-12.2.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a34329707af4f73cf1782a36cd2289c0368880654a2c11f027bcee9052d35dd", size = 6433935, upload-time = "2026-04-01T14:45:36.623Z" }, + { url = "https://files.pythonhosted.org/packages/6e/32/2880fb3a074847ac159d8f902cb43278a61e85f681661e7419e6596803ed/pillow-12.2.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e9c4f5b3c546fa3458a29ab22646c1c6c787ea8f5ef51300e5a60300736905e", size = 7116720, upload-time = "2026-04-01T14:45:39.258Z" }, + { url = "https://files.pythonhosted.org/packages/46/87/495cc9c30e0129501643f24d320076f4cc54f718341df18cc70ec94c44e1/pillow-12.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fb043ee2f06b41473269765c2feae53fc2e2fbf96e5e22ca94fb5ad677856f06", size = 6540498, upload-time = "2026-04-01T14:45:41.879Z" }, + { url = "https://files.pythonhosted.org/packages/18/53/773f5edca692009d883a72211b60fdaf8871cbef075eaa9d577f0a2f989e/pillow-12.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f278f034eb75b4e8a13a54a876cc4a5ab39173d2cdd93a638e1b467fc545ac43", size = 7239413, upload-time = "2026-04-01T14:45:44.705Z" }, + { url = "https://files.pythonhosted.org/packages/c9/e4/4b64a97d71b2a83158134abbb2f5bd3f8a2ea691361282f010998f339ec7/pillow-12.2.0-cp314-cp314t-win32.whl", hash = "sha256:6bb77b2dcb06b20f9f4b4a8454caa581cd4dd0643a08bacf821216a16d9c8354", size = 6482084, upload-time = "2026-04-01T14:45:47.568Z" }, + { url = "https://files.pythonhosted.org/packages/ba/13/306d275efd3a3453f72114b7431c877d10b1154014c1ebbedd067770d629/pillow-12.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6562ace0d3fb5f20ed7290f1f929cae41b25ae29528f2af1722966a0a02e2aa1", size = 7225152, upload-time = "2026-04-01T14:45:50.032Z" }, + { url = "https://files.pythonhosted.org/packages/ff/6e/cf826fae916b8658848d7b9f38d88da6396895c676e8086fc0988073aaf8/pillow-12.2.0-cp314-cp314t-win_arm64.whl", hash = "sha256:aa88ccfe4e32d362816319ed727a004423aab09c5cea43c01a4b435643fa34eb", size = 2556579, upload-time = "2026-04-01T14:45:52.529Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "psycopg2-binary" +version = "2.9.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cb/0e/bdc8274dc0585090b4e3432267d7be4dfbfd8971c0fa59167c711105a6bf/psycopg2-binary-2.9.10.tar.gz", hash = "sha256:4b3df0e6990aa98acda57d983942eff13d824135fe2250e6522edaa782a06de2", size = 385764, upload-time = "2024-10-16T11:24:58.126Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/7d/465cc9795cf76f6d329efdafca74693714556ea3891813701ac1fee87545/psycopg2_binary-2.9.10-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:880845dfe1f85d9d5f7c412efea7a08946a46894537e4e5d091732eb1d34d9a0", size = 3044771, upload-time = "2024-10-16T11:20:35.234Z" }, + { url = "https://files.pythonhosted.org/packages/8b/31/6d225b7b641a1a2148e3ed65e1aa74fc86ba3fee850545e27be9e1de893d/psycopg2_binary-2.9.10-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9440fa522a79356aaa482aa4ba500b65f28e5d0e63b801abf6aa152a29bd842a", size = 3275336, upload-time = "2024-10-16T11:20:38.742Z" }, + { url = "https://files.pythonhosted.org/packages/30/b7/a68c2b4bff1cbb1728e3ec864b2d92327c77ad52edcd27922535a8366f68/psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3923c1d9870c49a2d44f795df0c889a22380d36ef92440ff618ec315757e539", size = 2851637, upload-time = "2024-10-16T11:20:42.145Z" }, + { url = "https://files.pythonhosted.org/packages/0b/b1/cfedc0e0e6f9ad61f8657fd173b2f831ce261c02a08c0b09c652b127d813/psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b2c956c028ea5de47ff3a8d6b3cc3330ab45cf0b7c3da35a2d6ff8420896526", size = 3082097, upload-time = "2024-10-16T11:20:46.185Z" }, + { url = "https://files.pythonhosted.org/packages/18/ed/0a8e4153c9b769f59c02fb5e7914f20f0b2483a19dae7bf2db54b743d0d0/psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f758ed67cab30b9a8d2833609513ce4d3bd027641673d4ebc9c067e4d208eec1", size = 3264776, upload-time = "2024-10-16T11:20:50.879Z" }, + { url = "https://files.pythonhosted.org/packages/10/db/d09da68c6a0cdab41566b74e0a6068a425f077169bed0946559b7348ebe9/psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cd9b4f2cfab88ed4a9106192de509464b75a906462fb846b936eabe45c2063e", size = 3020968, upload-time = "2024-10-16T11:20:56.819Z" }, + { url = "https://files.pythonhosted.org/packages/94/28/4d6f8c255f0dfffb410db2b3f9ac5218d959a66c715c34cac31081e19b95/psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dc08420625b5a20b53551c50deae6e231e6371194fa0651dbe0fb206452ae1f", size = 2872334, upload-time = "2024-10-16T11:21:02.411Z" }, + { url = "https://files.pythonhosted.org/packages/05/f7/20d7bf796593c4fea95e12119d6cc384ff1f6141a24fbb7df5a668d29d29/psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d7cd730dfa7c36dbe8724426bf5612798734bff2d3c3857f36f2733f5bfc7c00", size = 2822722, upload-time = "2024-10-16T11:21:09.01Z" }, + { url = "https://files.pythonhosted.org/packages/4d/e4/0c407ae919ef626dbdb32835a03b6737013c3cc7240169843965cada2bdf/psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:155e69561d54d02b3c3209545fb08938e27889ff5a10c19de8d23eb5a41be8a5", size = 2920132, upload-time = "2024-10-16T11:21:16.339Z" }, + { url = "https://files.pythonhosted.org/packages/2d/70/aa69c9f69cf09a01da224909ff6ce8b68faeef476f00f7ec377e8f03be70/psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3cc28a6fd5a4a26224007712e79b81dbaee2ffb90ff406256158ec4d7b52b47", size = 2959312, upload-time = "2024-10-16T11:21:25.584Z" }, + { url = "https://files.pythonhosted.org/packages/d3/bd/213e59854fafe87ba47814bf413ace0dcee33a89c8c8c814faca6bc7cf3c/psycopg2_binary-2.9.10-cp312-cp312-win32.whl", hash = "sha256:ec8a77f521a17506a24a5f626cb2aee7850f9b69a0afe704586f63a464f3cd64", size = 1025191, upload-time = "2024-10-16T11:21:29.912Z" }, + { url = "https://files.pythonhosted.org/packages/92/29/06261ea000e2dc1e22907dbbc483a1093665509ea586b29b8986a0e56733/psycopg2_binary-2.9.10-cp312-cp312-win_amd64.whl", hash = "sha256:18c5ee682b9c6dd3696dad6e54cc7ff3a1a9020df6a5c0f861ef8bfd338c3ca0", size = 1164031, upload-time = "2024-10-16T11:21:34.211Z" }, + { url = "https://files.pythonhosted.org/packages/3e/30/d41d3ba765609c0763505d565c4d12d8f3c79793f0d0f044ff5a28bf395b/psycopg2_binary-2.9.10-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:26540d4a9a4e2b096f1ff9cce51253d0504dca5a85872c7f7be23be5a53eb18d", size = 3044699, upload-time = "2024-10-16T11:21:42.841Z" }, + { url = "https://files.pythonhosted.org/packages/35/44/257ddadec7ef04536ba71af6bc6a75ec05c5343004a7ec93006bee66c0bc/psycopg2_binary-2.9.10-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e217ce4d37667df0bc1c397fdcd8de5e81018ef305aed9415c3b093faaeb10fb", size = 3275245, upload-time = "2024-10-16T11:21:51.989Z" }, + { url = "https://files.pythonhosted.org/packages/1b/11/48ea1cd11de67f9efd7262085588790a95d9dfcd9b8a687d46caf7305c1a/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:245159e7ab20a71d989da00f280ca57da7641fa2cdcf71749c193cea540a74f7", size = 2851631, upload-time = "2024-10-16T11:21:57.584Z" }, + { url = "https://files.pythonhosted.org/packages/62/e0/62ce5ee650e6c86719d621a761fe4bc846ab9eff8c1f12b1ed5741bf1c9b/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c4ded1a24b20021ebe677b7b08ad10bf09aac197d6943bfe6fec70ac4e4690d", size = 3082140, upload-time = "2024-10-16T11:22:02.005Z" }, + { url = "https://files.pythonhosted.org/packages/27/ce/63f946c098611f7be234c0dd7cb1ad68b0b5744d34f68062bb3c5aa510c8/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3abb691ff9e57d4a93355f60d4f4c1dd2d68326c968e7db17ea96df3c023ef73", size = 3264762, upload-time = "2024-10-16T11:22:06.412Z" }, + { url = "https://files.pythonhosted.org/packages/43/25/c603cd81402e69edf7daa59b1602bd41eb9859e2824b8c0855d748366ac9/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8608c078134f0b3cbd9f89b34bd60a943b23fd33cc5f065e8d5f840061bd0673", size = 3020967, upload-time = "2024-10-16T11:22:11.583Z" }, + { url = "https://files.pythonhosted.org/packages/5f/d6/8708d8c6fca531057fa170cdde8df870e8b6a9b136e82b361c65e42b841e/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:230eeae2d71594103cd5b93fd29d1ace6420d0b86f4778739cb1a5a32f607d1f", size = 2872326, upload-time = "2024-10-16T11:22:16.406Z" }, + { url = "https://files.pythonhosted.org/packages/ce/ac/5b1ea50fc08a9df82de7e1771537557f07c2632231bbab652c7e22597908/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bb89f0a835bcfc1d42ccd5f41f04870c1b936d8507c6df12b7737febc40f0909", size = 2822712, upload-time = "2024-10-16T11:22:21.366Z" }, + { url = "https://files.pythonhosted.org/packages/c4/fc/504d4503b2abc4570fac3ca56eb8fed5e437bf9c9ef13f36b6621db8ef00/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f0c2d907a1e102526dd2986df638343388b94c33860ff3bbe1384130828714b1", size = 2920155, upload-time = "2024-10-16T11:22:25.684Z" }, + { url = "https://files.pythonhosted.org/packages/b2/d1/323581e9273ad2c0dbd1902f3fb50c441da86e894b6e25a73c3fda32c57e/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f8157bed2f51db683f31306aa497311b560f2265998122abe1dce6428bd86567", size = 2959356, upload-time = "2024-10-16T11:22:30.562Z" }, + { url = "https://files.pythonhosted.org/packages/08/50/d13ea0a054189ae1bc21af1d85b6f8bb9bbc5572991055d70ad9006fe2d6/psycopg2_binary-2.9.10-cp313-cp313-win_amd64.whl", hash = "sha256:27422aa5f11fbcd9b18da48373eb67081243662f9b46e6fd07c3eb46e4535142", size = 2569224, upload-time = "2025-01-04T20:09:19.234Z" }, +] + +[[package]] +name = "pydantic" +version = "2.10.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6a/c7/ca334c2ef6f2e046b1144fe4bb2a5da8a4c574e7f2ebf7e16b34a6a2fa92/pydantic-2.10.5.tar.gz", hash = "sha256:278b38dbbaec562011d659ee05f63346951b3a248a6f3642e1bc68894ea2b4ff", size = 761287, upload-time = "2025-01-09T13:33:25.929Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/58/26/82663c79010b28eddf29dcdd0ea723439535fa917fce5905885c0e9ba562/pydantic-2.10.5-py3-none-any.whl", hash = "sha256:4dd4e322dbe55472cb7ca7e73f4b63574eecccf2835ffa2af9021ce113c83c53", size = 431426, upload-time = "2025-01-09T13:33:22.312Z" }, +] + +[[package]] +name = "pydantic-core" +version = "2.27.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fc/01/f3e5ac5e7c25833db5eb555f7b7ab24cd6f8c322d3a3ad2d67a952dc0abc/pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39", size = 413443, upload-time = "2024-12-18T11:31:54.917Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d6/74/51c8a5482ca447871c93e142d9d4a92ead74de6c8dc5e66733e22c9bba89/pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0", size = 1893127, upload-time = "2024-12-18T11:28:30.346Z" }, + { url = "https://files.pythonhosted.org/packages/d3/f3/c97e80721735868313c58b89d2de85fa80fe8dfeeed84dc51598b92a135e/pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef", size = 1811340, upload-time = "2024-12-18T11:28:32.521Z" }, + { url = "https://files.pythonhosted.org/packages/9e/91/840ec1375e686dbae1bd80a9e46c26a1e0083e1186abc610efa3d9a36180/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7", size = 1822900, upload-time = "2024-12-18T11:28:34.507Z" }, + { url = "https://files.pythonhosted.org/packages/f6/31/4240bc96025035500c18adc149aa6ffdf1a0062a4b525c932065ceb4d868/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934", size = 1869177, upload-time = "2024-12-18T11:28:36.488Z" }, + { url = "https://files.pythonhosted.org/packages/fa/20/02fbaadb7808be578317015c462655c317a77a7c8f0ef274bc016a784c54/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6", size = 2038046, upload-time = "2024-12-18T11:28:39.409Z" }, + { url = "https://files.pythonhosted.org/packages/06/86/7f306b904e6c9eccf0668248b3f272090e49c275bc488a7b88b0823444a4/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c", size = 2685386, upload-time = "2024-12-18T11:28:41.221Z" }, + { url = "https://files.pythonhosted.org/packages/8d/f0/49129b27c43396581a635d8710dae54a791b17dfc50c70164866bbf865e3/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2", size = 1997060, upload-time = "2024-12-18T11:28:44.709Z" }, + { url = "https://files.pythonhosted.org/packages/0d/0f/943b4af7cd416c477fd40b187036c4f89b416a33d3cc0ab7b82708a667aa/pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4", size = 2004870, upload-time = "2024-12-18T11:28:46.839Z" }, + { url = "https://files.pythonhosted.org/packages/35/40/aea70b5b1a63911c53a4c8117c0a828d6790483f858041f47bab0b779f44/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3", size = 1999822, upload-time = "2024-12-18T11:28:48.896Z" }, + { url = "https://files.pythonhosted.org/packages/f2/b3/807b94fd337d58effc5498fd1a7a4d9d59af4133e83e32ae39a96fddec9d/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4", size = 2130364, upload-time = "2024-12-18T11:28:50.755Z" }, + { url = "https://files.pythonhosted.org/packages/fc/df/791c827cd4ee6efd59248dca9369fb35e80a9484462c33c6649a8d02b565/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57", size = 2158303, upload-time = "2024-12-18T11:28:54.122Z" }, + { url = "https://files.pythonhosted.org/packages/9b/67/4e197c300976af185b7cef4c02203e175fb127e414125916bf1128b639a9/pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc", size = 1834064, upload-time = "2024-12-18T11:28:56.074Z" }, + { url = "https://files.pythonhosted.org/packages/1f/ea/cd7209a889163b8dcca139fe32b9687dd05249161a3edda62860430457a5/pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9", size = 1989046, upload-time = "2024-12-18T11:28:58.107Z" }, + { url = "https://files.pythonhosted.org/packages/bc/49/c54baab2f4658c26ac633d798dab66b4c3a9bbf47cff5284e9c182f4137a/pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b", size = 1885092, upload-time = "2024-12-18T11:29:01.335Z" }, + { url = "https://files.pythonhosted.org/packages/41/b1/9bc383f48f8002f99104e3acff6cba1231b29ef76cfa45d1506a5cad1f84/pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b", size = 1892709, upload-time = "2024-12-18T11:29:03.193Z" }, + { url = "https://files.pythonhosted.org/packages/10/6c/e62b8657b834f3eb2961b49ec8e301eb99946245e70bf42c8817350cbefc/pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154", size = 1811273, upload-time = "2024-12-18T11:29:05.306Z" }, + { url = "https://files.pythonhosted.org/packages/ba/15/52cfe49c8c986e081b863b102d6b859d9defc63446b642ccbbb3742bf371/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9", size = 1823027, upload-time = "2024-12-18T11:29:07.294Z" }, + { url = "https://files.pythonhosted.org/packages/b1/1c/b6f402cfc18ec0024120602bdbcebc7bdd5b856528c013bd4d13865ca473/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9", size = 1868888, upload-time = "2024-12-18T11:29:09.249Z" }, + { url = "https://files.pythonhosted.org/packages/bd/7b/8cb75b66ac37bc2975a3b7de99f3c6f355fcc4d89820b61dffa8f1e81677/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1", size = 2037738, upload-time = "2024-12-18T11:29:11.23Z" }, + { url = "https://files.pythonhosted.org/packages/c8/f1/786d8fe78970a06f61df22cba58e365ce304bf9b9f46cc71c8c424e0c334/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a", size = 2685138, upload-time = "2024-12-18T11:29:16.396Z" }, + { url = "https://files.pythonhosted.org/packages/a6/74/d12b2cd841d8724dc8ffb13fc5cef86566a53ed358103150209ecd5d1999/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e", size = 1997025, upload-time = "2024-12-18T11:29:20.25Z" }, + { url = "https://files.pythonhosted.org/packages/a0/6e/940bcd631bc4d9a06c9539b51f070b66e8f370ed0933f392db6ff350d873/pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4", size = 2004633, upload-time = "2024-12-18T11:29:23.877Z" }, + { url = "https://files.pythonhosted.org/packages/50/cc/a46b34f1708d82498c227d5d80ce615b2dd502ddcfd8376fc14a36655af1/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27", size = 1999404, upload-time = "2024-12-18T11:29:25.872Z" }, + { url = "https://files.pythonhosted.org/packages/ca/2d/c365cfa930ed23bc58c41463bae347d1005537dc8db79e998af8ba28d35e/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee", size = 2130130, upload-time = "2024-12-18T11:29:29.252Z" }, + { url = "https://files.pythonhosted.org/packages/f4/d7/eb64d015c350b7cdb371145b54d96c919d4db516817f31cd1c650cae3b21/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1", size = 2157946, upload-time = "2024-12-18T11:29:31.338Z" }, + { url = "https://files.pythonhosted.org/packages/a4/99/bddde3ddde76c03b65dfd5a66ab436c4e58ffc42927d4ff1198ffbf96f5f/pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130", size = 1834387, upload-time = "2024-12-18T11:29:33.481Z" }, + { url = "https://files.pythonhosted.org/packages/71/47/82b5e846e01b26ac6f1893d3c5f9f3a2eb6ba79be26eef0b759b4fe72946/pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee", size = 1990453, upload-time = "2024-12-18T11:29:35.533Z" }, + { url = "https://files.pythonhosted.org/packages/51/b2/b2b50d5ecf21acf870190ae5d093602d95f66c9c31f9d5de6062eb329ad1/pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b", size = 1885186, upload-time = "2024-12-18T11:29:37.649Z" }, +] + +[[package]] +name = "pydantic-settings" +version = "2.7.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "python-dotenv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/73/7b/c58a586cd7d9ac66d2ee4ba60ca2d241fa837c02bca9bea80a9a8c3d22a9/pydantic_settings-2.7.1.tar.gz", hash = "sha256:10c9caad35e64bfb3c2fbf70a078c0e25cc92499782e5200747f942a065dec93", size = 79920, upload-time = "2024-12-31T11:27:44.632Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b4/46/93416fdae86d40879714f72956ac14df9c7b76f7d41a4d68aa9f71a0028b/pydantic_settings-2.7.1-py3-none-any.whl", hash = "sha256:590be9e6e24d06db33a4262829edef682500ef008565a969c73d39d5f8bfb3fd", size = 29718, upload-time = "2024-12-31T11:27:43.201Z" }, +] + +[[package]] +name = "pyjwt" +version = "2.13.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3b/81/58d0ac84e1ef3a3843791d6954d94c0b33d526c75eeb1efbce9d0a4c4077/pyjwt-2.13.0.tar.gz", hash = "sha256:41571c89ca91598c79e8ef18a2d07367d4810fbbd6f637794879baf1b7703423", size = 107515, upload-time = "2026-05-21T19:54:36.618Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl", hash = "sha256:66adcc2aff09b3f1bbd95fc1e1577df8ac8723c978552fd43304c8a290ac5728", size = 31274, upload-time = "2026-05-21T19:54:35.362Z" }, +] + +[[package]] +name = "pypdf" +version = "6.12.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0a/6d/20879428577c1e57ecd41b69dc86beabf43db9287ad2e702207f8b48c751/pypdf-6.12.2.tar.gz", hash = "sha256:111669eb6680c04495ae0c113a1476e3bf93a95761d23c7406b591c80a6490b1", size = 6468184, upload-time = "2026-05-26T13:31:26.911Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/44/fee070a16639d9869bb6a7e0f3a1b3946da1d66f32b9260b4d19cb90d7b2/pypdf-6.12.2-py3-none-any.whl", hash = "sha256:67b2699357a1f3f4c945940ea80826349ee507c9e2577724a14b4941982c104d", size = 343865, upload-time = "2026-05-26T13:31:25.068Z" }, +] + +[[package]] +name = "pytest" +version = "8.3.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/05/35/30e0d83068951d90a01852cb1cef56e5d8a09d20c7f511634cc2f7e0372a/pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761", size = 1445919, upload-time = "2024-12-01T12:54:25.98Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/92/76a1c94d3afee238333bc0a42b82935dd8f9cf8ce9e336ff87ee14d9e1cf/pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6", size = 343083, upload-time = "2024-12-01T12:54:19.735Z" }, +] + +[[package]] +name = "pytest-cov" +version = "6.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "coverage" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/be/45/9b538de8cef30e17c7b45ef42f538a94889ed6a16f2387a6c89e73220651/pytest-cov-6.0.0.tar.gz", hash = "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0", size = 66945, upload-time = "2024-10-29T20:13:35.363Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/36/3b/48e79f2cd6a61dbbd4807b4ed46cb564b4fd50a76166b1c4ea5c1d9e2371/pytest_cov-6.0.0-py3-none-any.whl", hash = "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35", size = 22949, upload-time = "2024-10-29T20:13:33.215Z" }, +] + +[[package]] +name = "pytest-django" +version = "4.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/02/c0/43c8b2528c24d7f1a48a47e3f7381f5ab2ae8c64634b0c3f4bd843063955/pytest_django-4.9.0.tar.gz", hash = "sha256:8bf7bc358c9ae6f6fc51b6cebb190fe20212196e6807121f11bd6a3b03428314", size = 84067, upload-time = "2024-09-02T15:49:18.407Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/fe/54f387ee1b41c9ad59e48fb8368a361fad0600fe404315e31a12bacaea7d/pytest_django-4.9.0-py3-none-any.whl", hash = "sha256:1d83692cb39188682dbb419ff0393867e9904094a549a7d38a3154d5731b2b99", size = 23723, upload-time = "2024-09-02T15:49:17.127Z" }, +] + +[[package]] +name = "python-dotenv" +version = "1.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bc/57/e84d88dfe0aec03b7a2d4327012c1627ab5f03652216c63d49846d7a6c58/python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca", size = 39115, upload-time = "2024-01-23T06:33:00.505Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/3e/b68c118422ec867fa7ab88444e1274aa40681c606d59ac27de5a5588f082/python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a", size = 19863, upload-time = "2024-01-23T06:32:58.246Z" }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + +[[package]] +name = "soupsieve" +version = "2.8.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/47/2c/0a5f6f8ee0d5589e48c7640213ed5175d52cf540a06725b628cc1a45d6ce/soupsieve-2.8.4.tar.gz", hash = "sha256:e121fd02e975c695e4e9e8774a5ee35d74714b59307868dcc5319ad2d9e3328e", size = 121110, upload-time = "2026-05-24T13:55:57.154Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/f5/0c41cb68dcae6b7de4fac4188a3a9589e21fb31df21ea3a2e888db95e6c9/soupsieve-2.8.4-py3-none-any.whl", hash = "sha256:e7e6b0769c8f51ed59acab6e994b00621096cfb1c640a7509295987388fbaf65", size = 37304, upload-time = "2026-05-24T13:55:55.406Z" }, +] + +[[package]] +name = "sqlparse" +version = "0.5.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/90/76/437d71068094df0726366574cf3432a4ed754217b436eb7429415cf2d480/sqlparse-0.5.5.tar.gz", hash = "sha256:e20d4a9b0b8585fdf63b10d30066c7c94c5d7a7ec47c889a2d83a3caa93ff28e", size = 120815, upload-time = "2025-12-19T07:17:45.073Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/4b/359f28a903c13438ef59ebeee215fb25da53066db67b305c125f1c6d2a25/sqlparse-0.5.5-py3-none-any.whl", hash = "sha256:12a08b3bf3eec877c519589833aed092e2444e68240a3577e8e26148acc7b1ba", size = 46138, upload-time = "2025-12-19T07:17:46.573Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] + +[[package]] +name = "tzdata" +version = "2026.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/19/1b9b0e29f30c6d35cb345486df41110984ea67ae69dddbc0e8a100999493/tzdata-2026.2.tar.gz", hash = "sha256:9173fde7d80d9018e02a662e168e5a2d04f87c41ea174b139fbef642eda62d10", size = 198254, upload-time = "2026-04-24T15:22:08.651Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/e4/dccd7f47c4b64213ac01ef921a1337ee6e30e8c6466046018326977efd95/tzdata-2026.2-py2.py3-none-any.whl", hash = "sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7", size = 349321, upload-time = "2026-04-24T15:22:05.876Z" }, +] diff --git a/docker-compose.yml b/docker-compose.yml index 0c72ebc..2836c1f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,9 +5,9 @@ services: volumes: - postgres_data:/var/lib/postgresql/data environment: - POSTGRES_DB: cloud_reader - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres + POSTGRES_DB: ${POSTGRES_DB:-cloud_reader} + POSTGRES_USER: ${POSTGRES_USER:-postgres} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres} ports: - "5432:5432" healthcheck: @@ -23,16 +23,16 @@ services: ports: - "8000:8000" environment: - DJANGO_SECRET_KEY: "dev-secret-key-change-in-production" - DJANGO_DEBUG: "True" - DB_NAME: cloud_reader - DB_USER: postgres - DB_PASSWORD: postgres - DB_HOST: db - DB_PORT: "5432" + DJANGO_SECRET_KEY: ${DJANGO_SECRET_KEY} + DJANGO_DEBUG: ${DJANGO_DEBUG:-True} + DB_NAME: ${POSTGRES_DB:-cloud_reader} + DB_USER: ${POSTGRES_USER:-postgres} + DB_PASSWORD: ${POSTGRES_PASSWORD:-postgres} + DB_HOST: ${DB_HOST:-db} + DB_PORT: ${DB_PORT:-5432} volumes: - ./backend:/app - - book_media:/app/media + - ./backend/media:/app/media command: > sh -c "python manage.py migrate && python manage.py runserver 0.0.0.0:8000" depends_on: @@ -46,7 +46,7 @@ services: ports: - "5173:5173" environment: - VITE_API_URL: "http://localhost:8000" + VITE_API_URL: ${VITE_API_URL:-http://localhost:8000} volumes: - ./frontend:/app - /app/node_modules @@ -54,5 +54,4 @@ services: - backend volumes: - postgres_data: - book_media: \ No newline at end of file + postgres_data: \ No newline at end of file diff --git a/docs/backend/010-open-library-metadata.md b/docs/backend/010-open-library-metadata.md new file mode 100644 index 0000000..7c74da0 --- /dev/null +++ b/docs/backend/010-open-library-metadata.md @@ -0,0 +1,117 @@ +# 010 — Open Library metadata on import + +**Status:** Implemented +**Created:** 2026-06-03 + +## Objective + +After a user uploads an EPUB/PDF (`POST /api/books/ebooks/`), enrich the `EBook` with metadata and a cover from [Open Library](https://openlibrary.org/developers/api), using only **title** and **author** from the upload form. Prefer **Spanish** editions when available; fall back to English/any. + +Upload must **never fail** if Open Library is down or no match is found. + +## Trigger + +- **Automatic:** `EBookUploadSerializer.create()` calls `enrich_ebook_metadata(ebook)` after file save. +- **Manual:** `POST /api/books/ebooks/{id}/enrich-metadata/` re-runs enrichment (owner only). + +## Open Library usage + +### Search + +`GET https://openlibrary.org/search.json` + +| Param | Value | +|-------|--------| +| `title` | User-provided title | +| `author` | User-provided author | +| `lang` | `es` (primary) or `en` (fallback) | +| `limit` | `5` | +| `fields` | `key,title,author_name,cover_i,first_publish_year,subject,language,edition_key,number_of_pages_median,publisher` | + +Primary pass also uses query filter `language:spa`. Fallback omits language filter. + +### Covers + +`GET https://covers.openlibrary.org/b/id/{cover_i}-L.jpg` — downloaded and stored on `EBook.cover_image`. + +## Match scoring + +| Score | Behavior | +|-------|----------| +| ≥ 0.8 | Apply OL title/author + cover + full metadata | +| 0.6 – 0.8 | Metadata + cover only; keep user title/author | +| < 0.6 | `match_status: not_found`; no field changes except metadata stub | + +Author overlap + title similarity (normalized strings, `difflib.SequenceMatcher`). Prefer hits with `cover_i`. + +## Data model + +No migration. Uses existing fields on `EBook`: + +- `metadata_json` — full enrichment payload (see below) +- `cover_image` — downloaded cover file +- `title` / `author` — updated when match score ≥ 0.8 + +### `metadata_json` shape + +```json +{ + "source": "openlibrary", + "matched_at": "2026-06-03T12:00:00+00:00", + "match_language": "es", + "match_score": 0.92, + "match_status": "matched", + "user_input": { "title": "...", "author": "..." }, + "openlibrary": { + "work_key": "/works/OL...", + "edition_key": "...", + "title": "...", + "authors": ["..."], + "cover_id": 12345, + "cover_url": "https://covers.openlibrary.org/b/id/12345-L.jpg", + "first_publish_year": 1605, + "subjects": ["..."], + "languages": ["spa"], + "publishers": ["..."], + "number_of_pages_median": 320 + } +} +``` + +## API changes + +### Upload response (unchanged path) + +`POST /api/books/ebooks/` — response may include populated `cover_image` and updated `title`/`author` after sync enrichment. + +### Detail + +`GET /api/books/ebooks/{id}/` — adds read-only `metadata` (alias of `metadata_json`). + +### Manual refresh + +`POST /api/books/ebooks/{id}/enrich-metadata/` — returns updated `EBookDetailSerializer` payload. + +## Configuration + +| Env var | Default | Description | +|---------|---------|-------------| +| `OPENLIBRARY_ENABLED` | `true` | Kill switch | +| `OPENLIBRARY_PREFERRED_LANG` | `es` | Primary `lang` param | +| `OPENLIBRARY_FALLBACK_LANG` | `en` | Fallback `lang` param | +| `OPENLIBRARY_TIMEOUT_SECONDS` | `5` | HTTP timeout | +| `OPENLIBRARY_USER_AGENT` | `CloudReader/1.0` | User-Agent header | + +## Code layout + +``` +backend/apps/books/services/ +├── openlibrary.py # Search, scoring, cover download +└── metadata.py # enrich_ebook_metadata orchestrator +``` + +## Verification + +1. Upload with title `Don Quijote`, author `Cervantes` → cover + Spanish-friendly metadata. +2. Upload with nonsense title/author → 201, no cover, `match_status: not_found`. +3. `POST .../enrich-metadata/` on existing ebook refreshes metadata. diff --git a/docs/001-customizable-mobile-reading-experience.md b/docs/frontend/001-customizable-mobile-reading-experience.md similarity index 100% rename from docs/001-customizable-mobile-reading-experience.md rename to docs/frontend/001-customizable-mobile-reading-experience.md diff --git a/docs/frontend/011-react-reader-web.md b/docs/frontend/011-react-reader-web.md new file mode 100644 index 0000000..8bff364 --- /dev/null +++ b/docs/frontend/011-react-reader-web.md @@ -0,0 +1,73 @@ +# 011 — Web reader (react-reader) + +**Status:** Implemented +**Created:** 2026-06-03 + +## Objective + +Replace the custom HTML chapter reader with [react-reader](https://github.com/gerhardsletten/react-reader) (epub.js) for paginated EPUB reading in the web app. Keep the existing toolbar and settings panel chrome. Block PDFs from the in-browser reader. + +## Architecture + +``` +Library (EPUB only) → /read/:id → ReadingPage + → GET /api/books/ebooks/{id}/ (metadata, format guard) + → GET /api/books/ebooks/{id}/file/ (authenticated EPUB blob) + → ReactReader (blob URL + CFI location) + → PATCH /api/books/ebooks/{id}/progress/ (epub_location + percentage) +``` + +EPUB files are fetched with JWT via the API, converted to a blob URL client-side, and passed to react-reader. This avoids unauthenticated `/media/` URLs and CORS issues in dev. + +## Backend changes + +### `GET /api/books/ebooks/{id}/file/` + +- Authenticated, owner-only +- Returns `FileResponse` with `Content-Type: application/epub+zip` +- Returns `400` if format is not `epub` + +### `ReadingProgress.epub_location` + +| Field | Type | Notes | +|-------|------|-------| +| `epub_location` | CharField(2048) | EPUB CFI string for resume position | + +Exposed on `GET/PATCH /api/books/ebooks/{id}/progress/` and in `EBookDetail.progress`. + +`current_position` stores whole-book percentage (0–100) derived from epub.js locations. + +## Frontend changes + +| File | Change | +|------|--------| +| `pages/ReadingPage.tsx` | `ReactReader` replaces chapter HTML rendering | +| `hooks/useEpubReader.ts` | Blob load, CFI state, debounced progress save | +| `utils/epubRendition.ts` | Theme/font application via `getRendition` | +| `api/books.ts` | `getEpubFile(id)` | +| `pages/Library.tsx` | Block PDF open with notice | +| `App.tsx` | Single route `/read/:id`; `/reader/:id` redirects | + +### Removed (web-only) + +- `useChapters.ts`, `TableOfContents.tsx`, `Reader.tsx`, `useReadingProgress.ts` + +Backend `/toc/` and `/content/` endpoints remain for mobile/API consumers. + +## EPUB-only policy + +- Library: clicking a PDF shows a dismissible notice; no navigation to reader +- ReadingPage: deep-link guard if `format !== 'epub'` +- Upload still accepts PDF for storage; web reader is EPUB-only + +## Dependencies + +- Frontend: `react-reader` (^2.0.15) + +## Verification + +1. Open an EPUB from library → paginated reading, swipe/tap page turns, built-in TOC +2. Close and reopen → resumes at saved CFI +3. Change theme/font in settings → applies inside epub iframe +4. Click a PDF in library → notice shown, reader not opened +5. `GET /api/books/ebooks/{id}/file/` without auth → 401 diff --git a/docs/frontend/012-library-context-menu.md b/docs/frontend/012-library-context-menu.md new file mode 100644 index 0000000..8408515 --- /dev/null +++ b/docs/frontend/012-library-context-menu.md @@ -0,0 +1,56 @@ +# 012 — Library book context menu + +**Status:** Implemented +**Created:** 2026-06-03 + +## Objective + +Add a custom right-click context menu on each book card in the library with **Sync metadata** (Open Library refresh) and **Remove** (delete from library). Show toast notifications for success, warning, and error outcomes. + +## Architecture + +``` +Library book card (contextmenu) + → BookContextMenu + → Sync metadata: POST /api/books/ebooks/{id}/enrich-metadata/ + → Remove: confirm dialog → DELETE /api/books/ebooks/{id}/ + → ToastProvider (app-wide) → success | warning | error toasts +``` + +Backend endpoints already exist; no API changes required. + +## Frontend changes + +| File | Change | +|------|--------| +| `api/books.ts` | `enrichEBookMetadata(id)` → `POST .../enrich-metadata/` | +| `types/book.ts` | `OpenLibraryMetadata` with `match_status` | +| `hooks/useToast.tsx` | `ToastProvider`, `showToast({ message, variant })`, auto-dismiss ~4s | +| `components/ToastContainer.tsx` | Fixed bottom-right toast stack | +| `components/BookContextMenu.tsx` | Positioned menu; sync + remove actions | +| `pages/Library.tsx` | `onContextMenu` on cards; local state updates | +| `App.tsx` | Wrap routes with `ToastProvider` | + +## Context menu behavior + +- Opens on right-click (`contextmenu`); browser default menu suppressed +- Position clamped to viewport; closes on outside click, Escape, or scroll +- **Sync metadata:** updates card title/author/cover from response; toast by `metadata.match_status`: + - `matched` → success + - `not_found` → warning + - other → neutral success +- **Remove:** `window.confirm` before delete; removes card from list on success +- Menu clicks do not trigger card navigation to reader + +## Dependencies + +None (no new npm packages). + +## Verification + +1. Right-click a book → menu appears at cursor +2. Sync metadata on a matched book → cover/title may update, success toast +3. Sync on unmatched book → warning toast, no crash +4. Remove → confirm → book disappears, success toast +5. Cancel remove → book stays, menu closes +6. Left-click still opens reader (EPUB) or PDF notice diff --git a/docs/frontend/013-i18n-web.md b/docs/frontend/013-i18n-web.md new file mode 100644 index 0000000..9ddde04 --- /dev/null +++ b/docs/frontend/013-i18n-web.md @@ -0,0 +1,54 @@ +# 013 — Frontend i18n (react-i18n-lite) + +**Status:** Implemented +**Created:** 2026-06-04 + +## Objective + +Internationalize the web UI in English (`en-US`) and Spanish (`es-ES`) using [react-i18n-lite](https://www.npmjs.com/package/react-i18n-lite). + +## Architecture + +``` +App.tsx + └── I18nProvider (TranslationContainer) + ├── resolveDefaultLanguage() — localStorage → navigator → en-US + ├── LanguagePersistence — sync setLanguage ↔ localStorage, html[lang] + └── AuthProvider → ToastProvider → routes +``` + +Components call `useTranslation()` and `t('namespace.key', { interpolation })`. + +## Locale files + +| File | Purpose | +|------|---------| +| `frontend/src/locales/en-US.ts` | English dictionary | +| `frontend/src/locales/es-ES.ts` | Spanish dictionary | +| `frontend/src/locales/index.ts` | `locales` map, `SupportedLanguage`, helpers | + +Key namespaces: `common`, `auth`, `library`, `contextMenu`, `toast`, `settings`, `reader`, `addBook`, `bookDetail`, `search`, `annotations`. + +## Language selection + +- **Settings page** — UI Language dropdown (English / Español) +- **Persistence** — `localStorage` key `cloud-reader.locale` +- **Default** — saved preference, else `navigator.language` (`es*` → `es-ES`), else `en-US` + +## Scope + +**Translated:** All user-facing chrome (library, auth, reader toolbar/settings/TOC, add book, book detail, annotations, context menu, toasts). + +**Not translated:** EPUB body content, API error bodies, backend `reading_status_display`, Open Library metadata fields. + +## Dependencies + +- `react-i18n-lite` (^1.0.10) + +## Verification + +1. Open app — labels match browser or saved language +2. Settings → switch to Español — library/auth update without reload +3. Refresh — language persists +4. Context menu and toasts show translated strings +5. `yarn build` succeeds diff --git a/docs/frontend/014-library-reading-progress.md b/docs/frontend/014-library-reading-progress.md new file mode 100644 index 0000000..cdcd8c0 --- /dev/null +++ b/docs/frontend/014-library-reading-progress.md @@ -0,0 +1,46 @@ +# 014 — Library reading progress badges + +**Status:** Implemented +**Created:** 2026-06-04 + +## Objective + +Show accurate reading state on library book cards using saved EPUB progress instead of always displaying "Want to Read" (`Quiero leer`). + +## Data source + +- `GET /api/books/ebooks/` returns `progress` (0–100) from `ReadingProgress.current_position` via `EBookListSerializer.get_progress`. +- Progress is updated by the web reader (`useEpubReader` → `PATCH .../progress/`). + +## Display rules + +| Progress | `opened` | Badge | Cover extra | +|----------|----------|-------|-------------| +| `null` or `0` | false | Want to Read | — | +| `0` | true | Opened | — | +| `1–98` | — | Reading · N% | Green progress bar on cover | +| `≥ 99` | — | Finished | — | + +**Opened** is set when the reader first displays (CFI saved, 0% progress). Turning pages moves to **Reading**. + +Threshold constant: `FINISHED_PROGRESS_THRESHOLD = 99` in `frontend/src/utils/libraryStatus.ts`. + +## Frontend changes + +| File | Change | +|------|--------| +| `utils/libraryStatus.ts` | `deriveReadingStatus`, `normalizeProgressPercent` | +| `pages/Library.tsx` | Map API progress → status; badge labels; client-side status filter | +| `locales/en-US.ts`, `es-ES.ts` | `library.readingStatus.readingWithProgress` | +| `pages/Library.module.css` | Cover progress bar styles | + +## Status filter + +The reading-status filter in the library panel now filters client-side by derived status (`want_to_read`, `reading`, `finished`). + +## Verification + +1. New upload → **Want to Read** / **Quiero leer** +2. Partial read → **Reading · N%** + progress bar +3. Near end (≥ 99%) → **Finished** / **Terminado** +4. Filters by Leyendo / Terminado work as expected diff --git a/docs/frontend/015-epub-bookmarks-notes.md b/docs/frontend/015-epub-bookmarks-notes.md new file mode 100644 index 0000000..6a547cb --- /dev/null +++ b/docs/frontend/015-epub-bookmarks-notes.md @@ -0,0 +1,83 @@ +# 015 — EPUB bookmarks and notes (physical-book UX) + +**Status:** Implemented +**Created:** 2026-06-04 + +## Objective + +Let users select passage text while reading an EPUB, save a bookmark (with optional thought), list markers in chapter order, and jump back to the exact location. Global view groups markers by book in a Reddit-style thread layout. + +## Fix: AnnotationsProvider + +`AnnotationsProvider` wraps all routes in [`App.tsx`](frontend/src/App.tsx) so `/bookmarks-notes` and the reader can use `useAnnotations()`. + +## Data model + +`Bookmark` (annotations app) references **`books.EBook`**, not catalog `Book`: + +| Field | Purpose | +|-------|---------| +| `ebook` | FK to uploaded ebook | +| `epub_cfi` | EPUB CFI anchor | +| `chapter_index` | Spine index for sort order | +| `chapter_title` | Display label | +| `location_text` | Selected passage quote | +| `content` | Optional user thought (empty = bookmark only) | +| `page` | Legacy display field (`chapter_index + 1`) | + +Unique: `(user, ebook, epub_cfi)`. + +Default API ordering: `chapter_index`, `epub_cfi`. + +Legacy `Note` model remains for old API; new UX uses `Bookmark.content` only. + +## Reader flow + +```mermaid +sequenceDiagram + participant User + participant EpubView + participant Popover + participant API + User->>EpubView: Select text + EpubView->>Popover: Show near selection + User->>Popover: Save optional thought + Popover->>API: POST /annotations/bookmarks/ +``` + +1. Text selection via epub.js `selected` event and content `mouseup` hook. +2. [`SelectionPopover`](frontend/src/components/reader/SelectionPopover.tsx) — floating UI, optional textarea. +3. Toolbar bookmark icon opens [`BookMarkersPanel`](frontend/src/components/reader/BookMarkersPanel.tsx) (current ebook only). +4. “Go to passage” sets reader `location` to stored CFI (`/read/:id` with router state). + +## Global page (`/bookmarks-notes`) + +[`MarkerThreadsView`](frontend/src/components/annotations/MarkerThreadsView.tsx): + +- Groups markers by ebook (collapsible book rows). +- Within each book: chapter order, passage as blockquote, thought indented below (Reddit-style). +- Optional filter: `/bookmarks-notes/:ebookId`. + +## API + +- `GET /api/annotations/bookmarks/?ebook={id}` +- `POST /api/annotations/bookmarks/` — body: `ebook`, `epub_cfi`, `chapter_index`, `chapter_title`, `location_text`, `content` + +## i18n + +New keys under `annotations.*` (EN/ES): `saveMarker`, `thoughtPlaceholder`, `bookmarkOnly`, `selectTextHint`, `goToPassage`, `inBookPanel`, `markerCount`, etc. + +## Verification + +1. `/bookmarks-notes` loads without provider error. +2. Select text in reader → popover → save with/without thought. +3. Markers appear in reader panel and global page under correct book, in chapter order. +4. “Go to passage” opens the correct location. + +See also [016 — Bookmark reading anchor](016-bookmark-reading-anchor.md) for preserving reading position while peeking at bookmarks. + +## Out of scope + +- PDF selection +- Multiple replies per passage +- Migrating legacy `Note` rows into `Bookmark` diff --git a/docs/frontend/016-bookmark-reading-anchor.md b/docs/frontend/016-bookmark-reading-anchor.md new file mode 100644 index 0000000..cb5843c --- /dev/null +++ b/docs/frontend/016-bookmark-reading-anchor.md @@ -0,0 +1,87 @@ +# 016 — Bookmark reading anchor + +**Status:** Implemented +**Created:** 2026-06-04 +**See also:** [015 — EPUB bookmarks and notes](015-epub-bookmarks-notes.md) + +## Objective + +Let users jump to a bookmark to review a passage without overwriting their true reading position. While peeking, show a high-visibility control to return to where they were reading. + +## Definitions + +| Term | Meaning | +|------|---------| +| **Reading anchor** | EPUB CFI (+ optional %) captured immediately before a bookmark peek | +| **Peek mode** | Temporary view at a bookmark location; server `ReadingProgress` is not updated | +| **Resume** | Jump back to the reading anchor and re-enable progress persistence | + +## Problem (before) + +“Go to passage” called `jumpToCfi`, which triggered `locationChanged` and debounced `PATCH /books/ebooks/{id}/progress/`, replacing `epub_location` and `current_position` with the bookmark. Library progress and the next reading session started at the bookmark instead of the real position. + +## Triggers (enter peek mode) + +- In-reader: **Go to passage** in [`BookMarkersPanel`](../frontend/src/components/reader/BookMarkersPanel.tsx) +- Global: **Go to passage** on [`/bookmarks-notes`](../frontend/src/components/annotations/BookmarksNotesPage.tsx) → `/read/:id` with `state.epubLocation` (bookmark CFI) + +## Non-triggers + +- Table of contents navigation +- Prev / next page buttons +- Creating a new bookmark from text selection +- Opening the book normally from the library (no `epubLocation` in router state) + +## UX + +### Resume control + +- Component: [`ResumeReadingButton`](../frontend/src/components/reader/ResumeReadingButton.tsx) +- Visible only when `isBookmarkPeekActive && readingAnchor != null` +- Position: right edge, **above** the next-page chevron (`.reader-page-nav--next`) +- Style: warm accent (`#ea580c` / `#f97316`), white label/icon; distinct from muted gray nav buttons +- Action: `resumeReadingAnchor()` — hides control, returns to anchor CFI +- i18n: `reader.resumeReading`, `reader.resumeReadingAria` + +### Anchor policy + +- First anchor is captured when peek starts; **additional “Go to passage” clicks while peeking do not replace the anchor** until the user resumes or leaves the reader. + +## Progress rules + +| Mode | `PATCH .../progress/` | +|------|------------------------| +| Normal reading | Yes (debounced on `locationChanged`, sync after locations ready) | +| Bookmark peek | **No** — `flushProgress` / `scheduleProgress` no-op | +| After resume | Yes — flush anchor CFI and percentage once | + +### Load from bookmarks page + +1. Fetch saved `ReadingProgress` from API. +2. If `state.epubLocation` is set and saved `epub_location` exists → store saved location as **anchor**, set peek mode, open at bookmark CFI. +3. Do not persist bookmark location as progress during peek. + +### In-reader peek + +1. Capture current `location` (CFI) as anchor (if valid). +2. Enter peek mode, jump to bookmark CFI. + +## API + +No backend changes in v1. Anchor is session-only in [`useEpubReader`](../frontend/src/hooks/useEpubReader.ts). + +## Verification + +1. Read to ~30%, open markers, **Go to passage** on an early bookmark → jumps; after debounce, library/API progress still reflects ~30% (not bookmark). +2. Orange **Back to reading** appears above the next chevron only during peek. +3. Tap resume → returns to ~30%; button hides; progress saves resume. +4. From `/bookmarks-notes`, **Go to passage** → peek + resume using saved progress as anchor. +5. TOC / prev / next do not show the resume button. +6. EN/ES strings present. + +## Out of scope + +- PDF reader +- Multiple anchor history stack +- Backend `resume_epub_location` field +- Peek mode for TOC jumps diff --git a/docs/backend/009-expo-integration.md b/docs/mobile/009-expo-integration.md similarity index 99% rename from docs/backend/009-expo-integration.md rename to docs/mobile/009-expo-integration.md index 44a5760..4de365f 100644 --- a/docs/backend/009-expo-integration.md +++ b/docs/mobile/009-expo-integration.md @@ -93,4 +93,4 @@ The monorepo structure supports a single pipeline that can: - `yarn install` at root (installs all workspaces) - `yarn workspace @cloud-reader/shared build` - `yarn workspace @cloud-reader/mobile build` (Expo EAS for mobile builds) -- `yarn workspace @cloud-reader/frontend build` (Vite for web builds) \ No newline at end of file +- `yarn workspace @cloud-reader/frontend build` (Vite for web builds) diff --git a/frontend/dist/assets/AddBook-DiryQGMk.js b/frontend/dist/assets/AddBook-DiryQGMk.js new file mode 100644 index 0000000..85772fd --- /dev/null +++ b/frontend/dist/assets/AddBook-DiryQGMk.js @@ -0,0 +1 @@ +import{X as j,h as S,r as d,j as e}from"./index-BQiEVoRj.js";import{b as B}from"./books-BG7wMiRx.js";import{g as v}from"./errors-bLl0IgZb.js";function R(){const{t:o}=j(),s=S(),[a,p]=d.useState(""),[c,b]=d.useState(""),[n,u]=d.useState(null),[r,f]=d.useState(!1),[x,i]=d.useState(null),y=t=>{var g,h;const l=((g=t.target.files)==null?void 0:g[0])??null;if(l){const m=(h=l.name.split(".").pop())==null?void 0:h.toLowerCase();if(m!=="epub"&&m!=="pdf"){i(o("addBook.fileTypeError")),u(null);return}u(l),i(null),a||p(l.name.replace(/\.(epub|pdf)$/i,"").slice(0,512))}},k=async t=>{if(t.preventDefault(),!n||!a.trim()){i(o("addBook.requiredError"));return}f(!0),i(null);try{await B.uploadEBook(n,a.trim(),c.trim()),s("/")}catch(l){i(v(l,o("addBook.uploadFailed")))}finally{f(!1)}};return e.jsxs("div",{style:{maxWidth:500,margin:"0 auto",padding:16,minHeight:"100vh",background:"#f8f9fa"},children:[e.jsxs("header",{style:{display:"flex",alignItems:"center",gap:12,marginBottom:24,padding:"16px 0",borderBottom:"1px solid #eee"},children:[e.jsxs("button",{onClick:()=>s("/"),style:{padding:"8px 16px",borderRadius:6,border:"1px solid #ddd",background:"#fff",cursor:"pointer",fontSize:14},children:["← ",o("common.back")]}),e.jsx("h1",{style:{fontSize:24,fontWeight:700,color:"#1a1a2e",margin:0},children:o("addBook.title")})]}),e.jsxs("form",{onSubmit:k,style:{display:"flex",flexDirection:"column",gap:20},children:[x&&e.jsx("div",{style:{background:"#fde8e8",padding:12,borderRadius:6,color:"#e74c3c",fontSize:14},children:x}),e.jsxs("div",{style:{display:"flex",flexDirection:"column",gap:6},children:[e.jsx("label",{style:{fontSize:14,fontWeight:600,color:"#555"},children:o("addBook.fileLabel")}),e.jsx("input",{type:"file",accept:".epub,.pdf",onChange:y,style:{padding:"10px 0"}}),n&&e.jsx("p",{style:{fontSize:13,color:"#666",marginTop:4},children:o("addBook.selected",{name:n.name})})]}),e.jsxs("div",{style:{display:"flex",flexDirection:"column",gap:6},children:[e.jsx("label",{style:{fontSize:14,fontWeight:600,color:"#555"},children:o("addBook.titleLabel")}),e.jsx("input",{type:"text",value:a,onChange:t=>p(t.target.value),placeholder:o("addBook.titlePlaceholder"),style:{padding:"10px 12px",borderRadius:6,border:"1px solid #ddd",fontSize:16,outline:"none"}})]}),e.jsxs("div",{style:{display:"flex",flexDirection:"column",gap:6},children:[e.jsx("label",{style:{fontSize:14,fontWeight:600,color:"#555"},children:o("addBook.authorLabel")}),e.jsx("input",{type:"text",value:c,onChange:t=>b(t.target.value),placeholder:o("addBook.authorPlaceholder"),style:{padding:"10px 12px",borderRadius:6,border:"1px solid #ddd",fontSize:16,outline:"none"}})]}),e.jsx("button",{type:"submit",disabled:r,style:{padding:"12px 24px",borderRadius:8,border:"none",background:"#1a1a2e",color:"#fff",fontSize:16,fontWeight:600,cursor:"pointer",opacity:r?.6:1,marginTop:8},children:o(r?"addBook.uploading":"addBook.upload")})]})]})}export{R as AddBookPage}; diff --git a/frontend/dist/assets/AuthPage-Dbb0UvrV.js b/frontend/dist/assets/AuthPage-Dbb0UvrV.js new file mode 100644 index 0000000..f001724 --- /dev/null +++ b/frontend/dist/assets/AuthPage-Dbb0UvrV.js @@ -0,0 +1 @@ +import{j as t,X as v,r as o,e as w,h as z}from"./index-BQiEVoRj.js";import{g as A}from"./errors-bLl0IgZb.js";function h({isLogin:n,onToggle:p}){const{t:e}=v(),[i,g]=o.useState(""),[a,f]=o.useState(""),[d,x]=o.useState(""),[c,s]=o.useState(null),[l,u]=o.useState(!1),{login:m,register:y}=w(),b=z(),j=async r=>{if(r.preventDefault(),s(null),!n&&a!==d){s(e("auth.passwordsMismatch"));return}u(!0);try{n?await m(i,a):await y(i,a),b("/")}catch(S){s(A(S,e(n?"auth.loginFailed":"auth.registrationFailed")))}finally{u(!1)}};return t.jsx("div",{style:{display:"flex",justifyContent:"center",alignItems:"center",minHeight:"100vh",background:"#f8f9fa",padding:16},children:t.jsxs("div",{style:{width:"100%",maxWidth:400,background:"#fff",borderRadius:12,padding:32,boxShadow:"0 2px 16px rgba(0,0,0,0.08)"},children:[t.jsx("h1",{style:{fontSize:28,fontWeight:700,color:"#1a1a2e",textAlign:"center",marginBottom:4},children:e("common.appName")}),t.jsx("h2",{style:{fontSize:16,color:"#888",textAlign:"center",marginBottom:24,fontWeight:400},children:e(n?"auth.signIn":"auth.createAccount")}),t.jsxs("form",{onSubmit:j,style:{display:"flex",flexDirection:"column",gap:16},children:[c&&t.jsx("div",{style:{background:"#fde8e8",padding:12,borderRadius:6,color:"#e74c3c",fontSize:14},children:c}),t.jsxs("div",{style:{display:"flex",flexDirection:"column",gap:6},children:[t.jsx("label",{style:{fontSize:14,fontWeight:600,color:"#555"},children:e("auth.email")}),t.jsx("input",{type:"email",value:i,onChange:r=>g(r.target.value),placeholder:e("auth.emailPlaceholder"),required:!0,style:{padding:"10px 12px",borderRadius:6,border:"1px solid #ddd",fontSize:16,outline:"none"}})]}),t.jsxs("div",{style:{display:"flex",flexDirection:"column",gap:6},children:[t.jsx("label",{style:{fontSize:14,fontWeight:600,color:"#555"},children:e("auth.password")}),t.jsx("input",{type:"password",value:a,onChange:r=>f(r.target.value),placeholder:e("auth.passwordPlaceholder"),required:!0,minLength:8,style:{padding:"10px 12px",borderRadius:6,border:"1px solid #ddd",fontSize:16,outline:"none"}})]}),!n&&t.jsxs("div",{style:{display:"flex",flexDirection:"column",gap:6},children:[t.jsx("label",{style:{fontSize:14,fontWeight:600,color:"#555"},children:e("auth.confirmPassword")}),t.jsx("input",{type:"password",value:d,onChange:r=>x(r.target.value),placeholder:e("auth.confirmPasswordPlaceholder"),required:!0,minLength:8,style:{padding:"10px 12px",borderRadius:6,border:"1px solid #ddd",fontSize:16,outline:"none"}})]}),t.jsx("button",{type:"submit",disabled:l,style:{padding:"12px 24px",borderRadius:8,border:"none",background:"#1a1a2e",color:"#fff",fontSize:16,fontWeight:600,cursor:"pointer",opacity:l?.6:1,marginTop:8},children:e(l?n?"auth.signingIn":"auth.creatingAccount":n?"auth.signIn":"auth.createAccount")})]}),t.jsxs("p",{style:{textAlign:"center",marginTop:20,color:"#888",fontSize:14},children:[n?e("auth.noAccount")+" ":e("auth.hasAccount")+" ",t.jsx("button",{onClick:p,style:{background:"none",border:"none",color:"#1a1a2e",fontWeight:600,cursor:"pointer",fontSize:14,textDecoration:"underline"},children:e(n?"auth.register":"auth.signIn")})]})]})})}function W({onToggle:n}){return t.jsx(h,{isLogin:!0,onToggle:n})}function k({onToggle:n}){return t.jsx(h,{isLogin:!1,onToggle:n})}export{W as LoginPage,k as RegisterPage}; diff --git a/frontend/dist/assets/BookDetailPage-B8jJgony.js b/frontend/dist/assets/BookDetailPage-B8jJgony.js new file mode 100644 index 0000000..d643dd9 --- /dev/null +++ b/frontend/dist/assets/BookDetailPage-B8jJgony.js @@ -0,0 +1 @@ +import{X as k,i as j,h as v,r,j as e}from"./index-BQiEVoRj.js";import{b as S}from"./books-BG7wMiRx.js";import{u as w,B}from"./useMediaQuery-DwhAHSnz.js";function _(){const{t:o}=k(),{id:a}=j(),s=v(),[i,p]=r.useState(null),[u,g]=r.useState(!0),[b,d]=r.useState(null),t=w(B.md),l=r.useCallback(async()=>{if(a){g(!0),d(null);try{const n=Number(a);if(Number.isNaN(n)){d(o("bookDetail.invalidId"));return}const y=await S.getBook(n);p(y)}catch(n){d(n instanceof Error?n.message:o("bookDetail.loadFailed"))}finally{g(!1)}}},[a,o]);r.useEffect(()=>{l()},[l]);const m={want_to_read:{bg:"#dbeafe",text:"#1d4ed8"},reading:{bg:"#dcfce7",text:"#16a34a"},finished:{bg:"#f3e8ff",text:"#9333ea"},dnf:{bg:"#fef3c7",text:"#b45309"}},c={maxWidth:720,margin:"0 auto",padding:t?16:24,minHeight:"100vh",background:"#f8f9fa"},h={display:"inline-flex",alignItems:"center",gap:4,padding:t?"10px 16px":"8px 16px",borderRadius:8,border:"1px solid #e5e7eb",background:"#fff",color:"#374151",fontSize:t?15:14,cursor:"pointer",marginBottom:t?16:24,minHeight:44};if(u)return e.jsxs("div",{style:c,children:[e.jsx("div",{style:{height:32,width:80,background:"#e5e7eb",borderRadius:6,marginBottom:24}}),e.jsxs("div",{style:{display:"flex",gap:t?16:24,flexDirection:t?"column":"row"},children:[e.jsx("div",{style:{width:t?140:240,height:t?210:360,background:"#e5e7eb",borderRadius:12,flexShrink:0,alignSelf:t?"center":"flex-start"}}),e.jsxs("div",{style:{flex:1},children:[e.jsx("div",{style:{height:28,background:"#e5e7eb",borderRadius:6,marginBottom:12,width:"60%"}}),e.jsx("div",{style:{height:18,background:"#e5e7eb",borderRadius:4,marginBottom:8,width:"40%"}}),e.jsx("div",{style:{height:18,background:"#e5e7eb",borderRadius:4,marginBottom:8,width:"30%"}}),e.jsx("div",{style:{height:14,background:"#e5e7eb",borderRadius:4,marginBottom:4,width:"90%"}}),e.jsx("div",{style:{height:14,background:"#e5e7eb",borderRadius:4,marginBottom:4,width:"80%"}}),e.jsx("div",{style:{height:14,background:"#e5e7eb",borderRadius:4,width:"70%"}})]})]})]});if(b||!i)return e.jsxs("div",{style:c,children:[e.jsxs("button",{onClick:()=>s("/"),style:h,children:["← ",o("bookDetail.backToLibrary")]}),e.jsxs("div",{style:{textAlign:"center",padding:t?"60px 16px":"80px 20px"},children:[e.jsx("div",{style:{fontSize:t?48:64,marginBottom:16},children:"😕"}),e.jsx("h2",{style:{fontSize:t?18:20,color:"#1f2937",marginBottom:8},children:o("bookDetail.notFound")}),e.jsx("p",{style:{color:"#6b7280",marginBottom:20},children:b||o("bookDetail.notFoundDescription")}),e.jsx("button",{onClick:()=>void l(),className:"btn",style:{padding:"12px 24px",fontSize:15,minHeight:44},children:o("common.retry")})]})]});const x=m[i.reading_status]??{bg:"#f3f4f6",text:"#6b7280"},f={year:"numeric",month:"long",day:"numeric"};return e.jsxs("div",{style:c,children:[e.jsxs("button",{onClick:()=>s("/"),style:h,children:["← ",o(t?"bookDetail.back":"bookDetail.backToLibrary")]}),e.jsxs("div",{style:{display:"flex",gap:t?20:32,flexDirection:t?"column":"row"},children:[e.jsx("div",{style:{flexShrink:0,alignSelf:t?"center":"flex-start"},children:e.jsx("div",{style:{width:t?160:240,height:t?240:360,borderRadius:12,overflow:"hidden",background:"#f0f0f0",display:"flex",alignItems:"center",justifyContent:"center",boxShadow:"0 4px 20px rgba(0,0,0,0.1)"},children:i.cover_image?e.jsx("img",{src:i.cover_image,alt:i.title,style:{width:"100%",height:"100%",objectFit:"cover"}}):e.jsx("span",{style:{fontSize:t?48:80},children:"📖"})})}),e.jsxs("div",{style:{flex:1,minWidth:0},children:[e.jsx("h1",{style:{fontSize:t?22:28,fontWeight:700,color:"#1f2937",marginBottom:8,lineHeight:1.2},children:i.title}),i.author&&e.jsx("p",{style:{fontSize:t?16:18,color:"#4b5563",marginBottom:6},children:o("bookDetail.byAuthor",{author:i.author})}),e.jsxs("div",{style:{display:"flex",gap:8,flexWrap:"wrap",marginBottom:16,marginTop:12},children:[e.jsx("span",{style:{background:x.bg,color:x.text,fontSize:13,fontWeight:600,padding:"4px 12px",borderRadius:999,minHeight:28,display:"inline-flex",alignItems:"center"},children:i.reading_status_display}),i.genre&&e.jsx("span",{style:{background:"#eef2ff",color:"#4f46e5",fontSize:13,padding:"4px 12px",borderRadius:999,minHeight:28,display:"inline-flex",alignItems:"center"},children:i.genre}),i.total_pages>0&&e.jsx("span",{style:{background:"#f3f4f6",color:"#6b7280",fontSize:13,padding:"4px 12px",borderRadius:999,minHeight:28,display:"inline-flex",alignItems:"center"},children:o("bookDetail.pages",{count:String(i.total_pages)})})]}),i.description&&e.jsxs("div",{style:{marginTop:20},children:[e.jsx("h3",{style:{fontSize:16,fontWeight:600,color:"#1f2937",marginBottom:8},children:o("bookDetail.description")}),e.jsx("p",{style:{fontSize:15,color:"#4b5563",lineHeight:1.7,whiteSpace:"pre-wrap"},children:i.description})]}),e.jsx("div",{style:{marginTop:24,paddingTop:16,borderTop:"1px solid #e5e7eb"},children:e.jsxs("p",{style:{fontSize:13,color:"#9ca3af"},children:[o("bookDetail.added",{date:new Date(i.created_at).toLocaleDateString(void 0,f)}),i.created_at!==i.updated_at&&` · ${o("bookDetail.updated",{date:new Date(i.updated_at).toLocaleDateString(void 0,f)})}`]})}),t&&e.jsx("div",{style:{marginTop:24},children:e.jsxs("button",{onClick:()=>s("/"),style:{width:"100%",padding:"14px 24px",borderRadius:10,border:"none",background:"#4f46e5",color:"#fff",fontSize:15,fontWeight:600,cursor:"pointer",minHeight:44},children:["← ",o("bookDetail.backToLibrary")]})})]})]})]})}export{_ as BookDetailPage}; diff --git a/frontend/dist/assets/BookmarksNotesPage-BpEBiOkO.js b/frontend/dist/assets/BookmarksNotesPage-BpEBiOkO.js new file mode 100644 index 0000000..5a7669d --- /dev/null +++ b/frontend/dist/assets/BookmarksNotesPage-BpEBiOkO.js @@ -0,0 +1 @@ +import{X as m,u,r as l,j as e,h as g,i as j}from"./index-BQiEVoRj.js";function N({ebookIdFilter:n,onGoToPassage:i}){const{t:s}=m(),{markersByBook:r,loadBookmarks:c,removeBookmark:k,state:h}=u(),[p,x]=l.useState({});l.useEffect(()=>{c(n)},[c,n]);const d=l.useMemo(()=>{if(!n)return r;const a=Number(n);return r.filter(o=>o.ebookId===a)},[r,n]);if(h.bookmarksLoading)return e.jsx("div",{className:"annotations-loading",children:s("annotations.loading")});if(d.length===0)return e.jsxs("div",{className:"annotations-empty",children:[e.jsx("p",{children:s("annotations.empty")}),e.jsx("p",{style:{fontSize:14,color:"#6b7280",marginTop:8},children:s("annotations.selectTextHint")})]});const b=a=>{x(o=>({...o,[a]:!o[a]}))};return e.jsx("div",{className:"marker-books-list",children:d.map(a=>{const o=p[a.ebookId]??!1;return e.jsxs("section",{className:"marker-book-group",children:[e.jsxs("button",{type:"button",className:"marker-book-header",onClick:()=>b(a.ebookId),children:[e.jsx("span",{className:"marker-book-title",children:a.ebookTitle}),e.jsx("span",{className:"marker-book-count",children:s("annotations.markerCount",{count:String(a.markers.length)})}),e.jsx("span",{className:"marker-book-chevron",children:o?"▸":"▾"})]}),!o&&e.jsx("ul",{className:"marker-thread-list",children:a.markers.map(t=>e.jsxs("li",{className:"marker-thread",children:[e.jsx("div",{className:"marker-thread-meta",children:t.chapter_title?e.jsx("span",{className:"marker-thread-chapter",children:t.chapter_title}):e.jsx("span",{className:"marker-thread-chapter",children:s("annotations.chapterIndex",{index:String(t.chapter_index+1)})})}),t.location_text&&e.jsxs("blockquote",{className:"annotation-quote marker-thread-passage",children:["“",t.location_text,"”"]}),t.content?e.jsx("p",{className:"marker-thread-thought marker-thread-reply",children:t.content}):e.jsx("span",{className:"annotation-kind-badge bookmark-badge",children:s("annotations.bookmarkOnly")}),e.jsxs("div",{className:"annotation-actions",children:[e.jsx("button",{type:"button",className:"btn btn-sm",onClick:()=>i(t.ebook_id,t.epub_cfi),children:s("annotations.goToPassage")}),e.jsx("button",{type:"button",className:"btn btn-sm btn-danger",onClick:()=>k(t.id),children:s("common.delete")})]})]},t.id))})]},a.ebookId)})})}function y(){const{t:n}=m(),i=g(),{bookId:s}=j();return e.jsxs("div",{className:"page bookmarks-notes-page",style:{maxWidth:720,margin:"0 auto",padding:16},children:[e.jsxs("header",{style:{display:"flex",alignItems:"center",gap:12,marginBottom:24},children:[e.jsxs("button",{type:"button",onClick:()=>i("/"),style:{padding:"8px 16px",borderRadius:6,border:"1px solid #ddd",background:"#fff",cursor:"pointer",fontSize:14},children:["← ",n("common.back")]}),e.jsx("h2",{style:{margin:0,fontSize:22,fontWeight:700},children:n("annotations.title")})]}),e.jsx(N,{ebookIdFilter:s,onGoToPassage:(r,c)=>{i(`/read/${r}`,{state:{epubLocation:c}})}})]})}export{y as BookmarksNotesPage}; diff --git a/frontend/dist/assets/Library-2D9sFJJ7.css b/frontend/dist/assets/Library-2D9sFJJ7.css new file mode 100644 index 0000000..c06c2d7 --- /dev/null +++ b/frontend/dist/assets/Library-2D9sFJJ7.css @@ -0,0 +1 @@ +._container_1fxkh_1{position:absolute;top:100%;left:0;right:0;z-index:100;background:#fff;border:1px solid #e5e7eb;border-top:none;border-radius:0 0 10px 10px;box-shadow:0 8px 24px #0000001f;max-height:320px;overflow-y:auto}._infoText_1fxkh_31{padding:12px 16px;color:#9ca3af;font-size:13px}._suggestionItem_1fxkh_43{display:flex;align-items:center;gap:12px;padding:10px 16px;cursor:pointer;border-bottom:1px solid #f3f4f6;min-height:44px}._suggestionItem_1fxkh_43:hover{background:#f9fafb}._coverImage_1fxkh_71{width:32px;height:48px;object-fit:cover;border-radius:4px}._coverPlaceholder_1fxkh_85{font-size:20px;flex-shrink:0}._bookInfo_1fxkh_95{min-width:0}._bookTitle_1fxkh_103{font-size:14px;font-weight:600;color:#1f2937;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}._bookAuthor_1fxkh_121{font-size:12px;color:#6b7280;margin-top:2px}._menu_ekpcj_1{position:fixed;z-index:9999;min-width:180px;background:#fff;border:1px solid #e5e7eb;border-radius:8px;box-shadow:0 4px 20px #0000001f;padding:4px 0;overflow:hidden}._item_ekpcj_25{display:block;width:100%;padding:10px 16px;border:none;background:transparent;text-align:left;font-size:14px;color:#374151;cursor:pointer;transition:background .1s}._item_ekpcj_25:hover:not(:disabled){background:#f3f4f6}._item_ekpcj_25:disabled{opacity:.5;cursor:not-allowed}._itemDanger_ekpcj_69{color:#dc2626}._itemDanger_ekpcj_69:hover:not(:disabled){background:#fef2f2}._itemLoading_ekpcj_85{color:#6b7280}._separator_ekpcj_93{height:1px;background:#e5e7eb;margin:4px 0}._bookCard_11p5a_1{background:#fff;border-radius:12px;overflow:hidden;box-shadow:0 2px 8px #0000000f;cursor:pointer;transition:transform .15s,box-shadow .15s}._bookCard_11p5a_1:hover{transform:translateY(-2px);box-shadow:0 4px 16px #0000001a}._coverProgressBar_11p5a_29{position:absolute;left:0;right:0;bottom:0;height:4px;background:#0000001f}._coverProgressFill_11p5a_47{height:100%;background:#16a34a;transition:width .2s ease} diff --git a/frontend/dist/assets/Library-Ci_rxC-B.js b/frontend/dist/assets/Library-Ci_rxC-B.js new file mode 100644 index 0000000..7d370ae --- /dev/null +++ b/frontend/dist/assets/Library-Ci_rxC-B.js @@ -0,0 +1 @@ +import{r,X as O,h as V,j as e,k as ce,e as de,d as ue}from"./index-BQiEVoRj.js";import{b as P}from"./books-BG7wMiRx.js";import{u as ge,B as fe}from"./useMediaQuery-DwhAHSnz.js";import{g as K}from"./errors-bLl0IgZb.js";const pe=99;function he(s,o=!1){return s!=null&&s>=pe?"finished":s!=null&&s>0?"reading":o?"opened":"want_to_read"}function me(s){if(s==null)return null;const o=Math.round(s);return o<=0?null:Math.min(100,o)}function J(s,o){const[n,h]=r.useState(s);return r.useEffect(()=>{const v=setTimeout(()=>h(s),o);return()=>clearTimeout(v)},[s,o]),n}function xe(){const[s,o]=r.useState(!1),[n,h]=r.useState(""),[v,k]=r.useState(!1),[c,m]=r.useState(!1),[j,S]=r.useState(null),y=r.useRef(null),d=r.useRef(!0);r.useEffect(()=>{d.current=!0;const E=window.SpeechRecognition??window.webkitSpeechRecognition;if(typeof E=="function"){k(!0);const _=new E;_.continuous=!1,_.interimResults=!1,_.lang="en-US",_.onresult=g=>{var C,R;const f=((R=(C=g.results[0])==null?void 0:C[0])==null?void 0:R.transcript)??"";d.current&&(h(f),m(!1),S(null))},_.onerror=g=>{d.current&&(m(!0),S(g.error),o(!1))},_.onend=()=>{d.current&&o(!1)},y.current=_}return()=>{d.current=!1,y.current&&y.current.abort()}},[]);const a=r.useCallback(()=>{if(y.current){h(""),m(!1),S(null);try{y.current.start(),o(!0)}catch{o(!1)}}},[]),l=r.useCallback(()=>{y.current&&(y.current.stop(),o(!1))},[]);return{isListening:s,transcript:n,isSupported:v,hasError:c,errorMessage:j,startListening:a,stopListening:l}}const be="_container_1fxkh_1",ye="_infoText_1fxkh_31",ve="_suggestionItem_1fxkh_43",Se="_coverImage_1fxkh_71",ke="_coverPlaceholder_1fxkh_85",je="_bookInfo_1fxkh_95",_e="_bookTitle_1fxkh_103",we="_bookAuthor_1fxkh_121",L={container:be,infoText:ye,suggestionItem:ve,coverImage:Se,coverPlaceholder:ke,bookInfo:je,bookTitle:_e,bookAuthor:we};function Ce({query:s,visible:o,onClose:n,onSelectSuggestion:h}){const{t:v}=O(),k=V(),[c,m]=r.useState([]),[j,S]=r.useState(!1),y=r.useRef(null),d=J(s,200);return r.useEffect(()=>{if(!d.trim()){m([]);return}let a=!1;return S(!0),P.searchBooks({q:d.trim(),page_size:5}).then(l=>{a||(m(l.results),S(!1))},()=>{a||(m([]),S(!1))}),()=>{a=!0}},[d]),r.useEffect(()=>{if(!o)return;const a=E=>{y.current&&!y.current.contains(E.target)&&n()},l=setTimeout(()=>document.addEventListener("click",a),0);return()=>{clearTimeout(l),document.removeEventListener("click",a)}},[o,n]),r.useEffect(()=>{if(!o)return;const a=l=>{l.key==="Escape"&&n()};return document.addEventListener("keydown",a),()=>document.removeEventListener("keydown",a)},[o,n]),!o||!s.trim()?null:e.jsxs("div",{ref:y,className:L.container,children:[j&&e.jsx("div",{className:L.infoText,children:v("search.searching")}),!j&&c.length===0&&d.trim()&&e.jsx("div",{className:L.infoText,children:v("search.noSuggestions")}),c.map(a=>e.jsxs("div",{className:L.suggestionItem,onClick:()=>{h(),k(`/books/${a.id}`)},children:[e.jsx("span",{className:L.coverPlaceholder,children:a.cover_image?e.jsx("img",{src:a.cover_image,alt:"",className:L.coverImage}):"📖"}),e.jsxs("div",{className:L.bookInfo,children:[e.jsx("div",{className:L.bookTitle,children:a.title}),e.jsx("div",{className:L.bookAuthor,children:a.author||v("common.unknownAuthor")})]})]},a.id))]})}const Re="_menu_ekpcj_1",Ee="_item_ekpcj_25",Le="_itemDanger_ekpcj_69",Be="_itemLoading_ekpcj_85",ze="_separator_ekpcj_93",B={menu:Re,item:Ee,itemDanger:Le,itemLoading:Be,separator:ze};function Pe(s,o,n,h){const k=window.innerWidth-n-8,c=window.innerHeight-h-8;return{left:Math.max(8,Math.min(s,k)),top:Math.max(8,Math.min(o,c))}}function Te({book:s,x:o,y:n,onClose:h,onBookUpdated:v,onBookRemoved:k}){const{t:c}=O(),{showToast:m}=ce(),j=r.useRef(null),[S,y]=r.useState({left:o,top:n}),[d,a]=r.useState(null);r.useLayoutEffect(()=>{const g=j.current;g&&y(Pe(o,n,g.offsetWidth,g.offsetHeight))},[o,n]),r.useEffect(()=>{const g=R=>{j.current&&!j.current.contains(R.target)&&h()},f=R=>{R.key==="Escape"&&h()},C=()=>h();return document.addEventListener("mousedown",g),document.addEventListener("keydown",f),window.addEventListener("scroll",C,!0),()=>{document.removeEventListener("mousedown",g),document.removeEventListener("keydown",f),window.removeEventListener("scroll",C,!0)}},[h]);const l=async()=>{var g;a("sync");try{const f=await P.enrichEBookMetadata(s.id);v({...s,id:f.id,title:f.title,author:f.author,genre:f.format?f.format.toUpperCase():"",format:f.format,cover_image:f.cover_image,progressPercent:s.progressPercent});const C=(g=f.metadata)==null?void 0:g.match_status;m(C==="matched"?{message:c("toast.metadataSynced",{title:f.title}),variant:"success"}:C==="not_found"?{message:c("toast.noMatch"),variant:"warning"}:{message:c("toast.refreshDone"),variant:"success"})}catch(f){m({message:K(f,c("common.unknownError")),variant:"error"})}finally{a(null),h()}},E=async()=>{if(window.confirm(c("contextMenu.confirmRemove",{title:s.title}))){a("remove");try{await P.deleteEBook(s.id),k(s.id),m({message:c("toast.removed",{title:s.title}),variant:"success"})}catch(f){m({message:K(f,c("common.unknownError")),variant:"error"})}finally{a(null),h()}}},_=d!==null;return e.jsxs("div",{ref:j,className:B.menu,style:{left:S.left,top:S.top},role:"menu",onClick:g=>g.stopPropagation(),onContextMenu:g=>g.preventDefault(),children:[e.jsx("button",{type:"button",className:`${B.item} ${d==="sync"?B.itemLoading:""}`,role:"menuitem",disabled:_,onClick:()=>void l(),children:c(d==="sync"?"contextMenu.syncingMetadata":"contextMenu.syncMetadata")}),e.jsx("div",{className:B.separator,role:"separator"}),e.jsx("button",{type:"button",className:`${B.item} ${B.itemDanger} ${d==="remove"?B.itemLoading:""}`,role:"menuitem",disabled:_,onClick:()=>void E(),children:c(d==="remove"?"contextMenu.removing":"contextMenu.remove")})]})}const Me="_bookCard_11p5a_1",Ne="_coverProgressBar_11p5a_29",Ie="_coverProgressFill_11p5a_47",$={bookCard:Me,coverProgressBar:Ne,coverProgressFill:Ie};function X(s,o){return s.reading_status==="reading"&&s.progressPercent!=null?o("library.readingStatus.readingWithProgress",{percent:String(s.progressPercent)}):o(ue(s.reading_status))}const z={minHeight:44,minWidth:44,display:"inline-flex",alignItems:"center",justifyContent:"center"};function De(){const s=V(),{logout:o}=de(),{t:n}=O(),[h,v]=r.useState([]),[k,c]=r.useState(!0),[m,j]=r.useState(null),[S,y]=r.useState(null),[d,a]=r.useState(""),[l,E]=r.useState({genre:"",author:"",reading_status:""}),[_,g]=r.useState([]),[f,C]=r.useState([]),[R,F]=r.useState(0),[N,U]=r.useState(!1),[Z,T]=r.useState(!1),[I,G]=r.useState(null),W=J(d,300),q=r.useRef(!1),ee=r.useRef(null),i=ge(fe.md),u=xe();r.useEffect(()=>{u.transcript&&!u.isListening&&(a(u.transcript),T(!1))},[u.transcript,u.isListening]),r.useEffect(()=>{q.current||(q.current=!0,Promise.all([P.getGenres(),P.getAuthors()]).then(([t,b])=>{g(t),C(b)},()=>{}))},[]);const H=r.useCallback(async t=>{var b;c(!0),j(null);try{let w=(await P.getEBooks()).map(p=>{const le=me(p.progress),Y=he(p.progress,!!p.opened);return{id:p.id,title:p.title,author:p.author,genre:p.format?p.format.toUpperCase():"",format:p.format,reading_status:Y,reading_status_display:Y,cover_image:p.cover_image,progressPercent:le}});const A=(b=t.q)==null?void 0:b.trim().toLowerCase();A&&(w=w.filter(p=>p.title.toLowerCase().includes(A)||p.author.toLowerCase().includes(A)||p.genre.toLowerCase().includes(A))),t.author&&(w=w.filter(p=>p.author===t.author)),t.genre&&(w=w.filter(p=>p.genre===t.genre)),t.reading_status&&(w=w.filter(p=>p.reading_status===t.reading_status)),v(w),F(w.length)}catch(x){j(x instanceof Error?x.message:n("library.loadFailed")),v([]),F(0)}finally{c(!1)}},[n]),te=[{value:"",label:n("library.allStatuses")},{value:"want_to_read",label:n("library.readingStatus.wantToRead")},{value:"opened",label:n("library.readingStatus.opened")},{value:"reading",label:n("library.readingStatus.reading")},{value:"finished",label:n("library.readingStatus.finished")},{value:"dnf",label:n("library.readingStatus.dnf")}];r.useEffect(()=>{const t={};W&&(t.q=W),l.genre&&(t.genre=l.genre),l.author&&(t.author=l.author),l.reading_status&&(t.reading_status=l.reading_status),H(t)},[W,l,H]);const D=(t,b)=>{E(x=>({...x,[t]:b}))},Q=()=>{a(""),E({genre:"",author:"",reading_status:""}),U(!1)},M=!!d||!!l.genre||!!l.author||!!l.reading_status,ne=r.useCallback(t=>{v(b=>b.map(x=>x.id===t.id?{...x,...t,progressPercent:t.progressPercent??x.progressPercent,reading_status:t.reading_status??x.reading_status}:x))},[]),re=r.useCallback(t=>{v(b=>b.filter(x=>x.id!==t)),F(b=>Math.max(0,b-1))},[]),se=r.useCallback((t,b)=>{t.preventDefault(),t.stopPropagation(),G({book:b,x:t.clientX,y:t.clientY})},[]),ie={maxWidth:960,margin:"0 auto",padding:i?12:16,minHeight:"100vh",background:"#f8f9fa"},oe={display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:i?12:20,padding:i?"12px 0":"16px 0",borderBottom:"1px solid #e5e7eb",flexWrap:"wrap",gap:8},ae={position:"relative",flex:1};return e.jsxs("div",{style:ie,children:[e.jsxs("header",{style:oe,children:[e.jsxs("div",{children:[e.jsx("h1",{style:{fontSize:i?20:24,fontWeight:700,color:"#1f2937",margin:0},children:n("library.title")}),!k&&e.jsx("p",{style:{fontSize:13,color:"#6b7280",marginTop:2},children:R===1?n("library.bookCountOne",{count:String(R)}):n("library.bookCountMany",{count:String(R)})})]}),e.jsx("div",{style:{display:"flex",gap:i?4:8,flexWrap:"wrap",alignItems:"center"},children:i?e.jsxs(e.Fragment,{children:[e.jsx("button",{onClick:()=>s("/add"),style:{...z,fontSize:20,background:"none",border:"none",cursor:"pointer",padding:8},title:n("library.addBook"),children:"➕"}),e.jsx("button",{onClick:()=>s("/bookmarks-notes"),style:{...z,fontSize:20,background:"none",border:"none",cursor:"pointer",padding:8},title:n("library.bookmarks"),children:"🔖"}),e.jsx("button",{onClick:()=>s("/settings"),style:{...z,fontSize:20,background:"none",border:"none",cursor:"pointer",padding:8},title:n("library.settings"),children:"⚙️"}),e.jsx("button",{onClick:o,style:{...z,fontSize:20,background:"none",border:"none",cursor:"pointer",padding:8},title:n("library.logout"),children:"🚪"})]}):e.jsxs(e.Fragment,{children:[e.jsxs("button",{onClick:()=>s("/add"),className:"btn",children:["+ ",n("library.addBook")]}),e.jsx("button",{onClick:()=>s("/bookmarks-notes"),className:"btn btn-secondary",children:n("library.bookmarks")}),e.jsx("button",{onClick:()=>s("/settings"),className:"btn btn-secondary",children:n("library.settings")}),e.jsx("button",{onClick:o,className:"btn btn-danger",children:n("library.logout")})]})})]}),e.jsx("div",{style:{marginBottom:16},children:e.jsxs("div",{style:{display:"flex",gap:8,alignItems:"center"},children:[e.jsxs("div",{style:ae,children:[e.jsx("input",{ref:ee,type:"text",placeholder:n("library.searchPlaceholder"),value:d,onChange:t=>{a(t.target.value),T(!0)},onFocus:()=>T(!0),style:{width:"100%",padding:`12px 16px 12px ${u.isSupported,44}px`,paddingRight:u.isSupported?48:16,borderRadius:10,border:"1px solid #e5e7eb",fontSize:i?16:15,background:"#fff",outline:"none",boxSizing:"border-box",minHeight:44}}),e.jsx("span",{style:{position:"absolute",left:14,top:"50%",transform:"translateY(-50%)",fontSize:18,color:"#9ca3af",pointerEvents:"none"},children:"🔍"}),u.isSupported&&e.jsx("button",{onClick:()=>{u.isListening?u.stopListening():u.startListening()},style:{position:"absolute",right:8,top:"50%",transform:"translateY(-50%)",background:u.isListening?"#dc2626":"transparent",border:"none",borderRadius:8,cursor:"pointer",fontSize:20,padding:"8px 8px",minWidth:36,minHeight:36,display:"flex",alignItems:"center",justifyContent:"center",color:u.isListening?"#fff":"#6b7280",transition:"background 0.15s"},title:u.isListening?n("library.voiceSearchStopTitle"):n("library.voiceSearchTitle"),children:"🎤"}),e.jsx(Ce,{query:d,visible:Z&&!u.isListening,onClose:()=>T(!1),onSelectSuggestion:()=>T(!1)})]}),e.jsx("button",{onClick:()=>U(!N),style:{...z,padding:"0 12px",borderRadius:8,border:"1px solid #e5e7eb",background:N?"#4f46e5":"#fff",color:N?"#fff":"#374151",fontSize:13,fontWeight:600,cursor:"pointer",whiteSpace:"nowrap",gap:4},children:i?"⚙️":`▼ ${n("library.filtersLabel")}`})]})}),u.isListening&&e.jsxs("div",{style:{background:"#fef2f2",padding:"10px 16px",borderRadius:8,marginBottom:12,display:"flex",alignItems:"center",gap:8,fontSize:14,color:"#dc2626"},children:[e.jsx("span",{style:{display:"inline-block",width:8,height:8,borderRadius:"50%",background:"#dc2626",animation:"pulse 1s infinite"}}),n("library.listening"),e.jsx("button",{onClick:u.stopListening,style:{marginLeft:"auto",background:"#dc2626",color:"#fff",border:"none",borderRadius:4,padding:"4px 12px",cursor:"pointer",fontSize:12},children:n("library.stop")})]}),u.hasError&&!u.isListening&&e.jsx("div",{style:{background:"#fef3c7",padding:"8px 12px",borderRadius:8,marginBottom:12,fontSize:13,color:"#92400e"},children:u.errorMessage==="no-speech"?n("library.voiceNoSpeech"):n("library.voiceError",{message:u.errorMessage??""})}),N&&e.jsxs("div",{style:{background:"#fff",borderRadius:10,padding:i?12:16,marginBottom:16,border:"1px solid #e5e7eb",display:"flex",flexDirection:i?"column":"row",gap:12,alignItems:i?"stretch":"end"},children:[e.jsxs("div",{style:{minWidth:i?0:160,flex:1},children:[e.jsx("label",{style:{display:"block",fontSize:12,fontWeight:600,color:"#6b7280",marginBottom:4},children:n("library.genre")}),e.jsxs("select",{value:l.genre,onChange:t=>D("genre",t.target.value),style:{width:"100%",padding:"8px 12px",borderRadius:6,border:"1px solid #e5e7eb",fontSize:14,background:"#fff",cursor:"pointer",minHeight:36},children:[e.jsx("option",{value:"",children:n("library.allGenres")}),_.map(t=>e.jsx("option",{value:t,children:t},t))]})]}),e.jsxs("div",{style:{minWidth:i?0:160,flex:1},children:[e.jsx("label",{style:{display:"block",fontSize:12,fontWeight:600,color:"#6b7280",marginBottom:4},children:n("library.author")}),e.jsxs("select",{value:l.author,onChange:t=>D("author",t.target.value),style:{width:"100%",padding:"8px 12px",borderRadius:6,border:"1px solid #e5e7eb",fontSize:14,background:"#fff",cursor:"pointer",minHeight:36},children:[e.jsx("option",{value:"",children:n("library.allAuthors")}),f.map(t=>e.jsx("option",{value:t,children:t},t))]})]}),e.jsxs("div",{style:{minWidth:i?0:160,flex:1},children:[e.jsx("label",{style:{display:"block",fontSize:12,fontWeight:600,color:"#6b7280",marginBottom:4},children:n("library.status")}),e.jsx("select",{value:l.reading_status,onChange:t=>D("reading_status",t.target.value),style:{width:"100%",padding:"8px 12px",borderRadius:6,border:"1px solid #e5e7eb",fontSize:14,background:"#fff",cursor:"pointer",minHeight:36},children:te.map(t=>e.jsx("option",{value:t.value,children:t.label},t.value))})]}),M&&e.jsxs("button",{onClick:Q,style:{...z,padding:"8px 16px",borderRadius:6,border:"1px solid #e5e7eb",background:"#fff",color:"#6b7280",fontSize:13,cursor:"pointer",whiteSpace:"nowrap",width:i?"100%":"auto"},children:["✕ ",n("library.clearFilters")]})]}),m&&e.jsxs("div",{style:{background:"#fef2f2",padding:16,borderRadius:8,marginBottom:16,textAlign:"center"},children:[e.jsx("p",{style:{color:"#dc2626",fontSize:14},children:m}),e.jsx("button",{onClick:()=>void H({}),style:{marginTop:8,padding:"8px 16px",border:"none",borderRadius:6,background:"#dc2626",color:"#fff",cursor:"pointer",fontSize:13,minHeight:36},children:n("common.retry")})]}),S&&e.jsxs("div",{style:{background:"#fffbeb",border:"1px solid #fde68a",borderRadius:8,padding:"12px 16px",marginBottom:16,display:"flex",justifyContent:"space-between",alignItems:"center",gap:12},children:[e.jsx("p",{style:{color:"#92400e",fontSize:14,margin:0},children:S}),e.jsx("button",{type:"button",onClick:()=>y(null),style:{padding:"4px 10px",border:"none",borderRadius:6,background:"#f59e0b",color:"#fff",cursor:"pointer",fontSize:12,flexShrink:0},children:n("common.dismiss")})]}),k&&e.jsx("div",{style:{display:"grid",gridTemplateColumns:i?"1fr":"repeat(auto-fill, minmax(200px, 1fr))",gap:i?12:16,opacity:.6},children:Array.from({length:i?4:8}).map((t,b)=>e.jsxs("div",{style:{background:"#fff",borderRadius:12,overflow:"hidden",boxShadow:"0 2px 8px rgba(0,0,0,0.06)",display:i?"flex":"block"},children:[e.jsx("div",{style:{width:i?80:"100%",height:i?120:180,background:"#f0f0f0",flexShrink:0}}),e.jsxs("div",{style:{padding:12},children:[e.jsx("div",{style:{height:14,background:"#f0f0f0",borderRadius:4,marginBottom:6,width:"70%"}}),e.jsx("div",{style:{height:12,background:"#f0f0f0",borderRadius:4,width:"40%"}})]})]},b))}),!k&&!m&&h.length===0&&e.jsxs("div",{style:{textAlign:"center",padding:i?"60px 16px":"80px 20px"},children:[e.jsx("div",{style:{fontSize:i?48:64,marginBottom:16},children:M?"🔍":"📚"}),e.jsx("h2",{style:{fontSize:i?18:20,color:"#1f2937",marginBottom:8},children:n(M?"library.emptyFilteredTitle":"library.emptyTitle")}),e.jsx("p",{style:{color:"#6b7280",marginBottom:20,fontSize:15,lineHeight:1.5},children:n(M?"library.emptyFilteredDescription":"library.emptyDescription")}),M?e.jsx("button",{onClick:Q,className:"btn",style:{padding:"12px 24px",fontSize:15,minHeight:44},children:n("library.clearAllFilters")}):e.jsx("button",{onClick:()=>s("/add"),className:"btn",style:{padding:"12px 24px",fontSize:15,minHeight:44},children:n("library.addFirstBook")})]}),!k&&h.length>0&&e.jsx("div",{style:{display:"grid",gridTemplateColumns:i?"1fr":"repeat(auto-fill, minmax(200px, 1fr))",gap:i?12:16},children:h.map(t=>{const x={want_to_read:{bg:"#dbeafe",text:"#1d4ed8"},opened:{bg:"#e0f2fe",text:"#0369a1"},reading:{bg:"#dcfce7",text:"#16a34a"},finished:{bg:"#f3e8ff",text:"#9333ea"},dnf:{bg:"#fef3c7",text:"#b45309"}}[t.reading_status]??{bg:"#f3f4f6",text:"#6b7280"};return e.jsxs("div",{onClick:()=>{if(t.format==="pdf"){y(n("library.pdfNotice"));return}s(`/read/${t.id}`)},onContextMenu:w=>se(w,t),className:$.bookCard,style:{display:i?"flex":"block"},children:[e.jsxs("div",{style:{width:i?80:"100%",height:i?120:180,background:"#f0f0f0",display:"flex",alignItems:"center",justifyContent:"center",position:"relative",flexShrink:0},children:[t.cover_image?e.jsx("img",{src:t.cover_image,alt:t.title,style:{width:"100%",height:"100%",objectFit:"cover"}}):e.jsx("span",{style:{fontSize:i?32:48},children:"📖"}),t.reading_status==="reading"&&t.progressPercent!=null&&e.jsx("div",{className:$.coverProgressBar,children:e.jsx("div",{className:$.coverProgressFill,style:{width:`${t.progressPercent}%`}})}),!i&&e.jsx("span",{style:{position:"absolute",top:8,right:8,background:x.bg,color:x.text,fontSize:11,fontWeight:600,padding:"2px 8px",borderRadius:999,lineHeight:"18px"},children:X(t,n)})]}),e.jsxs("div",{style:{padding:i?"8px 12px":12,flex:1},children:[e.jsxs("div",{style:{display:"flex",alignItems:"flex-start",justifyContent:"space-between",gap:8},children:[e.jsxs("div",{style:{minWidth:0,flex:1},children:[e.jsx("h3",{style:{fontSize:14,fontWeight:600,color:"#1f2937",marginBottom:2,margin:0,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:t.title}),e.jsx("p",{style:{fontSize:12,color:"#6b7280",marginBottom:4,margin:"2px 0"},children:t.author||n("common.unknownAuthor")})]}),i&&e.jsx("span",{style:{background:x.bg,color:x.text,fontSize:10,fontWeight:600,padding:"2px 6px",borderRadius:999,whiteSpace:"nowrap",flexShrink:0},children:X(t,n)})]}),t.genre&&e.jsx("span",{style:{fontSize:11,color:"#4f46e5",background:"#eef2ff",padding:"1px 6px",borderRadius:4,display:"inline-block",marginTop:4},children:t.genre})]})]},t.id)})}),I&&e.jsx(Te,{book:I.book,x:I.x,y:I.y,onClose:()=>G(null),onBookUpdated:ne,onBookRemoved:re})]})}export{De as LibraryPage}; diff --git a/frontend/dist/assets/ReadingPage-KSOW0yWe.js b/frontend/dist/assets/ReadingPage-KSOW0yWe.js new file mode 100644 index 0000000..be565e9 --- /dev/null +++ b/frontend/dist/assets/ReadingPage-KSOW0yWe.js @@ -0,0 +1,22 @@ +const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/ReaderToolbar-wmOf7G3U.js","assets/index-BQiEVoRj.js","assets/index-Bc1rr75j.css","assets/TableOfContents-BqoUCglr.js","assets/ReadingSettingsPanel-Bt3-VHc7.js"])))=>i.map(i=>d[i]); +import{g as Yt,c as bt,r as me,R as As,j as we,a as yi,X as wr,u as qn,k as Ds,i as Cs,h as Ss,f as Ts,_ as _r}from"./index-BQiEVoRj.js";import{b as jn}from"./books-BG7wMiRx.js";import{g as ks}from"./errors-bLl0IgZb.js";var ni={exports:{}},Ci={exports:{}},Si,Lr;function Wn(){if(Lr)return Si;Lr=1;var y=void 0;return Si=function(e){return e!==y&&e!==null},Si}var Ti,Br;function Ns(){if(Br)return Ti;Br=1;var y=Wn(),e={object:!0,function:!0,undefined:!0};return Ti=function(t){return y(t)?hasOwnProperty.call(e,typeof t):!1},Ti}var ki,Pr;function Rs(){if(Pr)return ki;Pr=1;var y=Ns();return ki=function(e){if(!y(e))return!1;try{return e.constructor?e.constructor.prototype===e:!1}catch{return!1}},ki}var Ni,Fr;function Is(){if(Fr)return Ni;Fr=1;var y=Rs();return Ni=function(e){if(typeof e!="function"||!hasOwnProperty.call(e,"length"))return!1;try{if(typeof e.length!="number"||typeof e.call!="function"||typeof e.apply!="function")return!1}catch{return!1}return!y(e)},Ni}var Ri,zr;function Os(){if(zr)return Ri;zr=1;var y=Is(),e=/^\s*class[\s{/}]/,t=Function.prototype.toString;return Ri=function(i){return!(!y(i)||e.test(t.call(i)))},Ri}var Ii,Mr;function Ls(){return Mr||(Mr=1,Ii=function(){var y=Object.assign,e;return typeof y!="function"?!1:(e={foo:"raz"},y(e,{bar:"dwa"},{trzy:"trzy"}),e.foo+e.bar+e.trzy==="razdwatrzy")}),Ii}var Oi,Ur;function Bs(){return Ur||(Ur=1,Oi=function(){try{return Object.keys("primitive"),!0}catch{return!1}}),Oi}var Li,qr;function Ps(){return qr||(qr=1,Li=function(){}),Li}var Bi,jr;function Er(){if(jr)return Bi;jr=1;var y=Ps()();return Bi=function(e){return e!==y&&e!==null},Bi}var Pi,Wr;function Fs(){if(Wr)return Pi;Wr=1;var y=Er(),e=Object.keys;return Pi=function(t){return e(y(t)?Object(t):t)},Pi}var Fi,Hr;function zs(){return Hr||(Hr=1,Fi=Bs()()?Object.keys:Fs()),Fi}var zi,Vr;function Ms(){if(Vr)return zi;Vr=1;var y=Er();return zi=function(e){if(!y(e))throw new TypeError("Cannot use null or undefined");return e},zi}var Mi,Gr;function Us(){if(Gr)return Mi;Gr=1;var y=zs(),e=Ms(),t=Math.max;return Mi=function(i,r){var n,s,a=t(arguments.length,2),u;for(i=Object(e(i)),u=function(h){try{i[h]=r[h]}catch(d){n||(n=d)}},s=1;s-1},Wi}var Hi,Kr;function Vs(){return Kr||(Kr=1,Hi=Ws()()?String.prototype.contains:Hs()),Hi}var Jr;function Gs(){if(Jr)return Ci.exports;Jr=1;var y=Wn(),e=Os(),t=qs(),i=js(),r=Vs(),n=Ci.exports=function(s,a){var u,h,d,c,w;return arguments.length<2||typeof s!="string"?(c=a,a=s,s=null):c=arguments[2],y(s)?(u=r.call(s,"c"),h=r.call(s,"e"),d=r.call(s,"w")):(u=d=!0,h=!1),w={value:a,configurable:u,enumerable:h,writable:d},c?t(i(c),w):w};return n.gs=function(s,a,u){var h,d,c,w;return typeof s!="string"?(c=u,u=a,a=s,s=null):c=arguments[3],y(a)?e(a)?y(u)?e(u)||(c=u,u=void 0):u=void 0:(c=a,a=u=void 0):a=void 0,y(s)?(h=r.call(s,"c"),d=r.call(s,"e")):(h=!0,d=!1),w={get:a,set:u,configurable:h,enumerable:d},c?t(i(c),w):w},Ci.exports}var Vi,Qr;function Zs(){return Qr||(Qr=1,Vi=function(y){if(typeof y!="function")throw new TypeError(y+" is not a function");return y}),Vi}var en;function Ys(){return en||(en=1,(function(y,e){var t=Gs(),i=Zs(),r=Function.prototype.apply,n=Function.prototype.call,s=Object.create,a=Object.defineProperty,u=Object.defineProperties,h=Object.prototype.hasOwnProperty,d={configurable:!0,enumerable:!1,writable:!0},c,w,m,N,E,f,v;c=function(g,D){var L;return i(D),h.call(this,"__ee__")?L=this.__ee__:(L=d.value=s(null),a(this,"__ee__",d),d.value=null),L[g]?typeof L[g]=="object"?L[g].push(D):L[g]=[L[g],D]:L[g]=D,this},w=function(g,D){var L,b;return i(D),b=this,c.call(this,g,L=function(){m.call(b,g,L),r.call(D,this,arguments)}),L.__eeOnceListener__=D,this},m=function(g,D){var L,b,S,R;if(i(D),!h.call(this,"__ee__"))return this;if(L=this.__ee__,!L[g])return this;if(b=L[g],typeof b=="object")for(R=0;S=b[R];++R)(S===D||S.__eeOnceListener__===D)&&(b.length===2?L[g]=b[R?0:1]:b.splice(R,1));else(b===D||b.__eeOnceListener__===D)&&delete L[g];return this},N=function(g){var D,L,b,S,R;if(h.call(this,"__ee__")&&(S=this.__ee__[g],!!S))if(typeof S=="object"){for(L=arguments.length,R=new Array(L-1),D=1;D=0&&p=0){for(var l=o.length-1;z0},lookupPrefix:function(p){for(var o=this;o;){var x=o._nsMap;if(x){for(var z in x)if(Object.prototype.hasOwnProperty.call(x,z)&&x[z]===p)return z}o=o.nodeType==w?o.ownerDocument:o.parentNode}return null},lookupNamespaceURI:function(p){for(var o=this;o;){var x=o._nsMap;if(x&&p in x&&Object.prototype.hasOwnProperty.call(x,p))return x[p];o=o.nodeType==w?o.ownerDocument:o.parentNode}return null},isDefaultNamespace:function(p){var o=this.lookupPrefix(p);return o==null}};function ce(p){return p=="<"&&"<"||p==">"&&">"||p=="&"&&"&"||p=='"'&&"""||"&#"+p.charCodeAt()+";"}u(d,ne),u(d,ne.prototype);function Ce(p,o){if(o(p))return!0;if(p=p.firstChild)do if(Ce(p,o))return!0;while(p=p.nextSibling)}function ye(){this.ownerDocument=this}function Te(p,o,x){p&&p._inc++;var z=x.namespaceURI;z===t.XMLNS&&(o._nsMap[x.prefix?x.localName:""]=x.value)}function We(p,o,x,z){p&&p._inc++;var l=x.namespaceURI;l===t.XMLNS&&delete o._nsMap[x.prefix?x.localName:""]}function Fe(p,o,x){if(p&&p._inc){p._inc++;var z=o.childNodes;if(x)z[z.length++]=x;else{for(var l=o.firstChild,M=0;l;)z[M++]=l,l=l.nextSibling;z.length=M,delete z[z.length]}}}function be(p,o){var x=o.previousSibling,z=o.nextSibling;return x?x.nextSibling=z:p.firstChild=z,z?z.previousSibling=x:p.lastChild=x,o.parentNode=null,o.previousSibling=null,o.nextSibling=null,Fe(p.ownerDocument,p),o}function Pe(p){return p&&(p.nodeType===ne.DOCUMENT_NODE||p.nodeType===ne.DOCUMENT_FRAGMENT_NODE||p.nodeType===ne.ELEMENT_NODE)}function Ue(p){return p&&(ke(p)||ve(p)||xe(p)||p.nodeType===ne.DOCUMENT_FRAGMENT_NODE||p.nodeType===ne.COMMENT_NODE||p.nodeType===ne.PROCESSING_INSTRUCTION_NODE)}function xe(p){return p&&p.nodeType===ne.DOCUMENT_TYPE_NODE}function ke(p){return p&&p.nodeType===ne.ELEMENT_NODE}function ve(p){return p&&p.nodeType===ne.TEXT_NODE}function Ie(p,o){var x=p.childNodes||[];if(e(x,ke)||xe(o))return!1;var z=e(x,xe);return!(o&&z&&x.indexOf(z)>x.indexOf(o))}function He(p,o){var x=p.childNodes||[];function z(M){return ke(M)&&M!==o}if(e(x,z))return!1;var l=e(x,xe);return!(o&&l&&x.indexOf(l)>x.indexOf(o))}function ze(p,o,x){if(!Pe(p))throw new j(k,"Unexpected parent node type "+p.nodeType);if(x&&x.parentNode!==p)throw new j(O,"child not in parent");if(!Ue(o)||xe(o)&&p.nodeType!==ne.DOCUMENT_NODE)throw new j(k,"Unexpected node type "+o.nodeType+" for parent node type "+p.nodeType)}function Oe(p,o,x){var z=p.childNodes||[],l=o.childNodes||[];if(o.nodeType===ne.DOCUMENT_FRAGMENT_NODE){var M=l.filter(ke);if(M.length>1||e(l,ve))throw new j(k,"More than one element or text in fragment");if(M.length===1&&!Ie(p,x))throw new j(k,"Element in fragment can not be inserted before doctype")}if(ke(o)&&!Ie(p,x))throw new j(k,"Only one element can be added and only after doctype");if(xe(o)){if(e(z,xe))throw new j(k,"Only one doctype is allowed");var H=e(z,ke);if(x&&z.indexOf(H)1||e(l,ve))throw new j(k,"More than one element or text in fragment");if(M.length===1&&!He(p,x))throw new j(k,"Element in fragment can not be inserted before doctype")}if(ke(o)&&!He(p,x))throw new j(k,"Only one element can be added and only after doctype");if(xe(o)){if(e(z,function(B){return xe(B)&&B!==x}))throw new j(k,"Only one doctype is allowed");var H=e(z,ke);if(x&&z.indexOf(H)0&&Ce(x.documentElement,function(l){if(l!==x&&l.nodeType===c){var M=l.getAttribute("class");if(M){var H=p===M;if(!H){var _=s(M);H=o.every(a(_))}H&&z.push(l)}}}),z})},createElement:function(p){var o=new Ee;o.ownerDocument=this,o.nodeName=p,o.tagName=p,o.localName=p,o.childNodes=new Z;var x=o.attributes=new ie;return x._ownerElement=o,o},createDocumentFragment:function(){var p=new ct;return p.ownerDocument=this,p.childNodes=new Z,p},createTextNode:function(p){var o=new nt;return o.ownerDocument=this,o.appendData(p),o},createComment:function(p){var o=new pt;return o.ownerDocument=this,o.appendData(p),o},createCDATASection:function(p){var o=new lt;return o.ownerDocument=this,o.appendData(p),o},createProcessingInstruction:function(p,o){var x=new ht;return x.ownerDocument=this,x.tagName=x.nodeName=x.target=p,x.nodeValue=x.data=o,x},createAttribute:function(p){var o=new De;return o.ownerDocument=this,o.name=p,o.nodeName=p,o.localName=p,o.specified=!0,o},createEntityReference:function(p){var o=new at;return o.ownerDocument=this,o.nodeName=p,o},createElementNS:function(p,o){var x=new Ee,z=o.split(":"),l=x.attributes=new ie;return x.childNodes=new Z,x.ownerDocument=this,x.nodeName=o,x.tagName=o,x.namespaceURI=p,z.length==2?(x.prefix=z[0],x.localName=z[1]):x.localName=o,l._ownerElement=x,x},createAttributeNS:function(p,o){var x=new De,z=o.split(":");return x.ownerDocument=this,x.nodeName=o,x.name=o,x.namespaceURI=p,x.specified=!0,z.length==2?(x.prefix=z[0],x.localName=z[1]):x.localName=o,x}},h(ye,ne);function Ee(){this._nsMap={}}Ee.prototype={nodeType:c,hasAttribute:function(p){return this.getAttributeNode(p)!=null},getAttribute:function(p){var o=this.getAttributeNode(p);return o&&o.value||""},getAttributeNode:function(p){return this.attributes.getNamedItem(p)},setAttribute:function(p,o){var x=this.ownerDocument.createAttribute(p);x.value=x.nodeValue=""+o,this.setAttributeNode(x)},removeAttribute:function(p){var o=this.getAttributeNode(p);o&&this.removeAttributeNode(o)},appendChild:function(p){return p.nodeType===b?this.insertBefore(p,null):Je(this,p)},setAttributeNode:function(p){return this.attributes.setNamedItem(p)},setAttributeNodeNS:function(p){return this.attributes.setNamedItemNS(p)},removeAttributeNode:function(p){return this.attributes.removeNamedItem(p.nodeName)},removeAttributeNS:function(p,o){var x=this.getAttributeNodeNS(p,o);x&&this.removeAttributeNode(x)},hasAttributeNS:function(p,o){return this.getAttributeNodeNS(p,o)!=null},getAttributeNS:function(p,o){var x=this.getAttributeNodeNS(p,o);return x&&x.value||""},setAttributeNS:function(p,o,x){var z=this.ownerDocument.createAttributeNS(p,o);z.value=z.nodeValue=""+x,this.setAttributeNode(z)},getAttributeNodeNS:function(p,o){return this.attributes.getNamedItemNS(p,o)},getElementsByTagName:function(p){return new Y(this,function(o){var x=[];return Ce(o,function(z){z!==o&&z.nodeType==c&&(p==="*"||z.tagName==p)&&x.push(z)}),x})},getElementsByTagNameNS:function(p,o){return new Y(this,function(x){var z=[];return Ce(x,function(l){l!==x&&l.nodeType===c&&(p==="*"||l.namespaceURI===p)&&(o==="*"||l.localName==o)&&z.push(l)}),z})}},ye.prototype.getElementsByTagName=Ee.prototype.getElementsByTagName,ye.prototype.getElementsByTagNameNS=Ee.prototype.getElementsByTagNameNS,h(Ee,ne);function De(){}De.prototype.nodeType=w,h(De,ne);function _e(){}_e.prototype={data:"",substringData:function(p,o){return this.data.substring(p,p+o)},appendData:function(p){p=this.data+p,this.nodeValue=this.data=p,this.length=p.length},insertData:function(p,o){this.replaceData(p,0,o)},appendChild:function(p){throw new Error(I[k])},deleteData:function(p,o){this.replaceData(p,o,"")},replaceData:function(p,o,x){var z=this.data.substring(0,p),l=this.data.substring(p+o);x=z+x+l,this.nodeValue=this.data=x,this.length=x.length}},h(_e,ne);function nt(){}nt.prototype={nodeName:"#text",nodeType:m,splitText:function(p){var o=this.data,x=o.substring(p);o=o.substring(0,p),this.data=this.nodeValue=o,this.length=o.length;var z=this.ownerDocument.createTextNode(x);return this.parentNode&&this.parentNode.insertBefore(z,this.nextSibling),z}},h(nt,_e);function pt(){}pt.prototype={nodeName:"#comment",nodeType:g},h(pt,_e);function lt(){}lt.prototype={nodeName:"#cdata-section",nodeType:N},h(lt,_e);function it(){}it.prototype.nodeType=L,h(it,ne);function vt(){}vt.prototype.nodeType=S,h(vt,ne);function ft(){}ft.prototype.nodeType=f,h(ft,ne);function at(){}at.prototype.nodeType=E,h(at,ne);function ct(){}ct.prototype.nodeName="#document-fragment",ct.prototype.nodeType=b,h(ct,ne);function ht(){}ht.prototype.nodeType=v,h(ht,ne);function Ye(){}Ye.prototype.serializeToString=function(p,o,x){return ot.call(p,o,x)},ne.prototype.toString=ot;function ot(p,o){var x=[],z=this.nodeType==9&&this.documentElement||this,l=z.prefix,M=z.namespaceURI;if(M&&l==null){var l=z.lookupPrefix(M);if(l==null)var H=[{namespace:M,prefix:null}]}return Q(this,x,p,o,H),x.join("")}function wt(p,o,x){var z=p.prefix||"",l=p.namespaceURI;if(!l||z==="xml"&&l===t.XML||l===t.XMLNS)return!1;for(var M=x.length;M--;){var H=x[M];if(H.prefix===z)return H.namespace!==l}return!0}function C(p,o,x){p.push(" ",o,'="',x.replace(/[<&"]/g,ce),'"')}function Q(p,o,x,z,l){if(l||(l=[]),z)if(p=z(p),p){if(typeof p=="string"){o.push(p);return}}else return;switch(p.nodeType){case c:var M=p.attributes,H=M.length,Ne=p.firstChild,_=p.tagName;x=t.isHTML(p.namespaceURI)||x;var B=_;if(!x&&!p.prefix&&p.namespaceURI){for(var V,de=0;de=0;ue--){var le=l[ue];if(le.prefix===""&&le.namespace===p.namespaceURI){V=le.namespace;break}}if(V!==p.namespaceURI)for(var ue=l.length-1;ue>=0;ue--){var le=l[ue];if(le.namespace===p.namespaceURI){le.prefix&&(B=le.prefix+":"+_);break}}}o.push("<",B);for(var Ae=0;Ae"),x&&/^script$/i.test(_))for(;Ne;)Ne.data?o.push(Ne.data):Q(Ne,o,x,z,l.slice()),Ne=Ne.nextSibling;else for(;Ne;)Q(Ne,o,x,z,l.slice()),Ne=Ne.nextSibling;o.push("")}else o.push("/>");return;case D:case b:for(var Ne=p.firstChild;Ne;)Q(Ne,o,x,z,l.slice()),Ne=Ne.nextSibling;return;case w:return C(o,p.name,p.value);case m:return o.push(p.data.replace(/[<&]/g,ce).replace(/]]>/g,"]]>"));case N:return o.push("");case g:return o.push("");case L:var Qe=p.publicId,Ze=p.systemId;if(o.push("");else if(Ze&&Ze!=".")o.push(" SYSTEM ",Ze,">");else{var Tt=p.internalSubset;Tt&&o.push(" [",Tt,"]"),o.push(">")}return;case v:return o.push("");case E:return o.push("&",p.nodeName,";");default:o.push("??",p.nodeName)}}function te(p,o,x){var z;switch(o.nodeType){case c:z=o.cloneNode(!1),z.ownerDocument=p;case b:break;case w:x=!0;break}if(z||(z=o.cloneNode(!1)),z.ownerDocument=p,z.parentNode=null,x)for(var l=o.firstChild;l;)z.appendChild(te(p,l,x)),l=l.nextSibling;return z}function fe(p,o,x){var z=new o.constructor;for(var l in o)if(Object.prototype.hasOwnProperty.call(o,l)){var M=o[l];typeof M!="object"&&M!=z[l]&&(z[l]=M)}switch(o.childNodes&&(z.childNodes=new Z),z.ownerDocument=p,z.nodeType){case c:var H=o.attributes,_=z.attributes=new ie,B=H.length;_._ownerElement=z;for(var V=0;V",lt:"<",quot:'"'}),y.HTML_ENTITIES=e({Aacute:"Á",aacute:"á",Abreve:"Ă",abreve:"ă",ac:"∾",acd:"∿",acE:"∾̳",Acirc:"Â",acirc:"â",acute:"´",Acy:"А",acy:"а",AElig:"Æ",aelig:"æ",af:"⁡",Afr:"𝔄",afr:"𝔞",Agrave:"À",agrave:"à",alefsym:"ℵ",aleph:"ℵ",Alpha:"Α",alpha:"α",Amacr:"Ā",amacr:"ā",amalg:"⨿",AMP:"&",amp:"&",And:"⩓",and:"∧",andand:"⩕",andd:"⩜",andslope:"⩘",andv:"⩚",ang:"∠",ange:"⦤",angle:"∠",angmsd:"∡",angmsdaa:"⦨",angmsdab:"⦩",angmsdac:"⦪",angmsdad:"⦫",angmsdae:"⦬",angmsdaf:"⦭",angmsdag:"⦮",angmsdah:"⦯",angrt:"∟",angrtvb:"⊾",angrtvbd:"⦝",angsph:"∢",angst:"Å",angzarr:"⍼",Aogon:"Ą",aogon:"ą",Aopf:"𝔸",aopf:"𝕒",ap:"≈",apacir:"⩯",apE:"⩰",ape:"≊",apid:"≋",apos:"'",ApplyFunction:"⁡",approx:"≈",approxeq:"≊",Aring:"Å",aring:"å",Ascr:"𝒜",ascr:"𝒶",Assign:"≔",ast:"*",asymp:"≈",asympeq:"≍",Atilde:"Ã",atilde:"ã",Auml:"Ä",auml:"ä",awconint:"∳",awint:"⨑",backcong:"≌",backepsilon:"϶",backprime:"‵",backsim:"∽",backsimeq:"⋍",Backslash:"∖",Barv:"⫧",barvee:"⊽",Barwed:"⌆",barwed:"⌅",barwedge:"⌅",bbrk:"⎵",bbrktbrk:"⎶",bcong:"≌",Bcy:"Б",bcy:"б",bdquo:"„",becaus:"∵",Because:"∵",because:"∵",bemptyv:"⦰",bepsi:"϶",bernou:"ℬ",Bernoullis:"ℬ",Beta:"Β",beta:"β",beth:"ℶ",between:"≬",Bfr:"𝔅",bfr:"𝔟",bigcap:"⋂",bigcirc:"◯",bigcup:"⋃",bigodot:"⨀",bigoplus:"⨁",bigotimes:"⨂",bigsqcup:"⨆",bigstar:"★",bigtriangledown:"▽",bigtriangleup:"△",biguplus:"⨄",bigvee:"⋁",bigwedge:"⋀",bkarow:"⤍",blacklozenge:"⧫",blacksquare:"▪",blacktriangle:"▴",blacktriangledown:"▾",blacktriangleleft:"◂",blacktriangleright:"▸",blank:"␣",blk12:"▒",blk14:"░",blk34:"▓",block:"█",bne:"=⃥",bnequiv:"≡⃥",bNot:"⫭",bnot:"⌐",Bopf:"𝔹",bopf:"𝕓",bot:"⊥",bottom:"⊥",bowtie:"⋈",boxbox:"⧉",boxDL:"╗",boxDl:"╖",boxdL:"╕",boxdl:"┐",boxDR:"╔",boxDr:"╓",boxdR:"╒",boxdr:"┌",boxH:"═",boxh:"─",boxHD:"╦",boxHd:"╤",boxhD:"╥",boxhd:"┬",boxHU:"╩",boxHu:"╧",boxhU:"╨",boxhu:"┴",boxminus:"⊟",boxplus:"⊞",boxtimes:"⊠",boxUL:"╝",boxUl:"╜",boxuL:"╛",boxul:"┘",boxUR:"╚",boxUr:"╙",boxuR:"╘",boxur:"└",boxV:"║",boxv:"│",boxVH:"╬",boxVh:"╫",boxvH:"╪",boxvh:"┼",boxVL:"╣",boxVl:"╢",boxvL:"╡",boxvl:"┤",boxVR:"╠",boxVr:"╟",boxvR:"╞",boxvr:"├",bprime:"‵",Breve:"˘",breve:"˘",brvbar:"¦",Bscr:"ℬ",bscr:"𝒷",bsemi:"⁏",bsim:"∽",bsime:"⋍",bsol:"\\",bsolb:"⧅",bsolhsub:"⟈",bull:"•",bullet:"•",bump:"≎",bumpE:"⪮",bumpe:"≏",Bumpeq:"≎",bumpeq:"≏",Cacute:"Ć",cacute:"ć",Cap:"⋒",cap:"∩",capand:"⩄",capbrcup:"⩉",capcap:"⩋",capcup:"⩇",capdot:"⩀",CapitalDifferentialD:"ⅅ",caps:"∩︀",caret:"⁁",caron:"ˇ",Cayleys:"ℭ",ccaps:"⩍",Ccaron:"Č",ccaron:"č",Ccedil:"Ç",ccedil:"ç",Ccirc:"Ĉ",ccirc:"ĉ",Cconint:"∰",ccups:"⩌",ccupssm:"⩐",Cdot:"Ċ",cdot:"ċ",cedil:"¸",Cedilla:"¸",cemptyv:"⦲",cent:"¢",CenterDot:"·",centerdot:"·",Cfr:"ℭ",cfr:"𝔠",CHcy:"Ч",chcy:"ч",check:"✓",checkmark:"✓",Chi:"Χ",chi:"χ",cir:"○",circ:"ˆ",circeq:"≗",circlearrowleft:"↺",circlearrowright:"↻",circledast:"⊛",circledcirc:"⊚",circleddash:"⊝",CircleDot:"⊙",circledR:"®",circledS:"Ⓢ",CircleMinus:"⊖",CirclePlus:"⊕",CircleTimes:"⊗",cirE:"⧃",cire:"≗",cirfnint:"⨐",cirmid:"⫯",cirscir:"⧂",ClockwiseContourIntegral:"∲",CloseCurlyDoubleQuote:"”",CloseCurlyQuote:"’",clubs:"♣",clubsuit:"♣",Colon:"∷",colon:":",Colone:"⩴",colone:"≔",coloneq:"≔",comma:",",commat:"@",comp:"∁",compfn:"∘",complement:"∁",complexes:"ℂ",cong:"≅",congdot:"⩭",Congruent:"≡",Conint:"∯",conint:"∮",ContourIntegral:"∮",Copf:"ℂ",copf:"𝕔",coprod:"∐",Coproduct:"∐",COPY:"©",copy:"©",copysr:"℗",CounterClockwiseContourIntegral:"∳",crarr:"↵",Cross:"⨯",cross:"✗",Cscr:"𝒞",cscr:"𝒸",csub:"⫏",csube:"⫑",csup:"⫐",csupe:"⫒",ctdot:"⋯",cudarrl:"⤸",cudarrr:"⤵",cuepr:"⋞",cuesc:"⋟",cularr:"↶",cularrp:"⤽",Cup:"⋓",cup:"∪",cupbrcap:"⩈",CupCap:"≍",cupcap:"⩆",cupcup:"⩊",cupdot:"⊍",cupor:"⩅",cups:"∪︀",curarr:"↷",curarrm:"⤼",curlyeqprec:"⋞",curlyeqsucc:"⋟",curlyvee:"⋎",curlywedge:"⋏",curren:"¤",curvearrowleft:"↶",curvearrowright:"↷",cuvee:"⋎",cuwed:"⋏",cwconint:"∲",cwint:"∱",cylcty:"⌭",Dagger:"‡",dagger:"†",daleth:"ℸ",Darr:"↡",dArr:"⇓",darr:"↓",dash:"‐",Dashv:"⫤",dashv:"⊣",dbkarow:"⤏",dblac:"˝",Dcaron:"Ď",dcaron:"ď",Dcy:"Д",dcy:"д",DD:"ⅅ",dd:"ⅆ",ddagger:"‡",ddarr:"⇊",DDotrahd:"⤑",ddotseq:"⩷",deg:"°",Del:"∇",Delta:"Δ",delta:"δ",demptyv:"⦱",dfisht:"⥿",Dfr:"𝔇",dfr:"𝔡",dHar:"⥥",dharl:"⇃",dharr:"⇂",DiacriticalAcute:"´",DiacriticalDot:"˙",DiacriticalDoubleAcute:"˝",DiacriticalGrave:"`",DiacriticalTilde:"˜",diam:"⋄",Diamond:"⋄",diamond:"⋄",diamondsuit:"♦",diams:"♦",die:"¨",DifferentialD:"ⅆ",digamma:"ϝ",disin:"⋲",div:"÷",divide:"÷",divideontimes:"⋇",divonx:"⋇",DJcy:"Ђ",djcy:"ђ",dlcorn:"⌞",dlcrop:"⌍",dollar:"$",Dopf:"𝔻",dopf:"𝕕",Dot:"¨",dot:"˙",DotDot:"⃜",doteq:"≐",doteqdot:"≑",DotEqual:"≐",dotminus:"∸",dotplus:"∔",dotsquare:"⊡",doublebarwedge:"⌆",DoubleContourIntegral:"∯",DoubleDot:"¨",DoubleDownArrow:"⇓",DoubleLeftArrow:"⇐",DoubleLeftRightArrow:"⇔",DoubleLeftTee:"⫤",DoubleLongLeftArrow:"⟸",DoubleLongLeftRightArrow:"⟺",DoubleLongRightArrow:"⟹",DoubleRightArrow:"⇒",DoubleRightTee:"⊨",DoubleUpArrow:"⇑",DoubleUpDownArrow:"⇕",DoubleVerticalBar:"∥",DownArrow:"↓",Downarrow:"⇓",downarrow:"↓",DownArrowBar:"⤓",DownArrowUpArrow:"⇵",DownBreve:"̑",downdownarrows:"⇊",downharpoonleft:"⇃",downharpoonright:"⇂",DownLeftRightVector:"⥐",DownLeftTeeVector:"⥞",DownLeftVector:"↽",DownLeftVectorBar:"⥖",DownRightTeeVector:"⥟",DownRightVector:"⇁",DownRightVectorBar:"⥗",DownTee:"⊤",DownTeeArrow:"↧",drbkarow:"⤐",drcorn:"⌟",drcrop:"⌌",Dscr:"𝒟",dscr:"𝒹",DScy:"Ѕ",dscy:"ѕ",dsol:"⧶",Dstrok:"Đ",dstrok:"đ",dtdot:"⋱",dtri:"▿",dtrif:"▾",duarr:"⇵",duhar:"⥯",dwangle:"⦦",DZcy:"Џ",dzcy:"џ",dzigrarr:"⟿",Eacute:"É",eacute:"é",easter:"⩮",Ecaron:"Ě",ecaron:"ě",ecir:"≖",Ecirc:"Ê",ecirc:"ê",ecolon:"≕",Ecy:"Э",ecy:"э",eDDot:"⩷",Edot:"Ė",eDot:"≑",edot:"ė",ee:"ⅇ",efDot:"≒",Efr:"𝔈",efr:"𝔢",eg:"⪚",Egrave:"È",egrave:"è",egs:"⪖",egsdot:"⪘",el:"⪙",Element:"∈",elinters:"⏧",ell:"ℓ",els:"⪕",elsdot:"⪗",Emacr:"Ē",emacr:"ē",empty:"∅",emptyset:"∅",EmptySmallSquare:"◻",emptyv:"∅",EmptyVerySmallSquare:"▫",emsp:" ",emsp13:" ",emsp14:" ",ENG:"Ŋ",eng:"ŋ",ensp:" ",Eogon:"Ę",eogon:"ę",Eopf:"𝔼",eopf:"𝕖",epar:"⋕",eparsl:"⧣",eplus:"⩱",epsi:"ε",Epsilon:"Ε",epsilon:"ε",epsiv:"ϵ",eqcirc:"≖",eqcolon:"≕",eqsim:"≂",eqslantgtr:"⪖",eqslantless:"⪕",Equal:"⩵",equals:"=",EqualTilde:"≂",equest:"≟",Equilibrium:"⇌",equiv:"≡",equivDD:"⩸",eqvparsl:"⧥",erarr:"⥱",erDot:"≓",Escr:"ℰ",escr:"ℯ",esdot:"≐",Esim:"⩳",esim:"≂",Eta:"Η",eta:"η",ETH:"Ð",eth:"ð",Euml:"Ë",euml:"ë",euro:"€",excl:"!",exist:"∃",Exists:"∃",expectation:"ℰ",ExponentialE:"ⅇ",exponentiale:"ⅇ",fallingdotseq:"≒",Fcy:"Ф",fcy:"ф",female:"♀",ffilig:"ffi",fflig:"ff",ffllig:"ffl",Ffr:"𝔉",ffr:"𝔣",filig:"fi",FilledSmallSquare:"◼",FilledVerySmallSquare:"▪",fjlig:"fj",flat:"♭",fllig:"fl",fltns:"▱",fnof:"ƒ",Fopf:"𝔽",fopf:"𝕗",ForAll:"∀",forall:"∀",fork:"⋔",forkv:"⫙",Fouriertrf:"ℱ",fpartint:"⨍",frac12:"½",frac13:"⅓",frac14:"¼",frac15:"⅕",frac16:"⅙",frac18:"⅛",frac23:"⅔",frac25:"⅖",frac34:"¾",frac35:"⅗",frac38:"⅜",frac45:"⅘",frac56:"⅚",frac58:"⅝",frac78:"⅞",frasl:"⁄",frown:"⌢",Fscr:"ℱ",fscr:"𝒻",gacute:"ǵ",Gamma:"Γ",gamma:"γ",Gammad:"Ϝ",gammad:"ϝ",gap:"⪆",Gbreve:"Ğ",gbreve:"ğ",Gcedil:"Ģ",Gcirc:"Ĝ",gcirc:"ĝ",Gcy:"Г",gcy:"г",Gdot:"Ġ",gdot:"ġ",gE:"≧",ge:"≥",gEl:"⪌",gel:"⋛",geq:"≥",geqq:"≧",geqslant:"⩾",ges:"⩾",gescc:"⪩",gesdot:"⪀",gesdoto:"⪂",gesdotol:"⪄",gesl:"⋛︀",gesles:"⪔",Gfr:"𝔊",gfr:"𝔤",Gg:"⋙",gg:"≫",ggg:"⋙",gimel:"ℷ",GJcy:"Ѓ",gjcy:"ѓ",gl:"≷",gla:"⪥",glE:"⪒",glj:"⪤",gnap:"⪊",gnapprox:"⪊",gnE:"≩",gne:"⪈",gneq:"⪈",gneqq:"≩",gnsim:"⋧",Gopf:"𝔾",gopf:"𝕘",grave:"`",GreaterEqual:"≥",GreaterEqualLess:"⋛",GreaterFullEqual:"≧",GreaterGreater:"⪢",GreaterLess:"≷",GreaterSlantEqual:"⩾",GreaterTilde:"≳",Gscr:"𝒢",gscr:"ℊ",gsim:"≳",gsime:"⪎",gsiml:"⪐",Gt:"≫",GT:">",gt:">",gtcc:"⪧",gtcir:"⩺",gtdot:"⋗",gtlPar:"⦕",gtquest:"⩼",gtrapprox:"⪆",gtrarr:"⥸",gtrdot:"⋗",gtreqless:"⋛",gtreqqless:"⪌",gtrless:"≷",gtrsim:"≳",gvertneqq:"≩︀",gvnE:"≩︀",Hacek:"ˇ",hairsp:" ",half:"½",hamilt:"ℋ",HARDcy:"Ъ",hardcy:"ъ",hArr:"⇔",harr:"↔",harrcir:"⥈",harrw:"↭",Hat:"^",hbar:"ℏ",Hcirc:"Ĥ",hcirc:"ĥ",hearts:"♥",heartsuit:"♥",hellip:"…",hercon:"⊹",Hfr:"ℌ",hfr:"𝔥",HilbertSpace:"ℋ",hksearow:"⤥",hkswarow:"⤦",hoarr:"⇿",homtht:"∻",hookleftarrow:"↩",hookrightarrow:"↪",Hopf:"ℍ",hopf:"𝕙",horbar:"―",HorizontalLine:"─",Hscr:"ℋ",hscr:"𝒽",hslash:"ℏ",Hstrok:"Ħ",hstrok:"ħ",HumpDownHump:"≎",HumpEqual:"≏",hybull:"⁃",hyphen:"‐",Iacute:"Í",iacute:"í",ic:"⁣",Icirc:"Î",icirc:"î",Icy:"И",icy:"и",Idot:"İ",IEcy:"Е",iecy:"е",iexcl:"¡",iff:"⇔",Ifr:"ℑ",ifr:"𝔦",Igrave:"Ì",igrave:"ì",ii:"ⅈ",iiiint:"⨌",iiint:"∭",iinfin:"⧜",iiota:"℩",IJlig:"IJ",ijlig:"ij",Im:"ℑ",Imacr:"Ī",imacr:"ī",image:"ℑ",ImaginaryI:"ⅈ",imagline:"ℐ",imagpart:"ℑ",imath:"ı",imof:"⊷",imped:"Ƶ",Implies:"⇒",in:"∈",incare:"℅",infin:"∞",infintie:"⧝",inodot:"ı",Int:"∬",int:"∫",intcal:"⊺",integers:"ℤ",Integral:"∫",intercal:"⊺",Intersection:"⋂",intlarhk:"⨗",intprod:"⨼",InvisibleComma:"⁣",InvisibleTimes:"⁢",IOcy:"Ё",iocy:"ё",Iogon:"Į",iogon:"į",Iopf:"𝕀",iopf:"𝕚",Iota:"Ι",iota:"ι",iprod:"⨼",iquest:"¿",Iscr:"ℐ",iscr:"𝒾",isin:"∈",isindot:"⋵",isinE:"⋹",isins:"⋴",isinsv:"⋳",isinv:"∈",it:"⁢",Itilde:"Ĩ",itilde:"ĩ",Iukcy:"І",iukcy:"і",Iuml:"Ï",iuml:"ï",Jcirc:"Ĵ",jcirc:"ĵ",Jcy:"Й",jcy:"й",Jfr:"𝔍",jfr:"𝔧",jmath:"ȷ",Jopf:"𝕁",jopf:"𝕛",Jscr:"𝒥",jscr:"𝒿",Jsercy:"Ј",jsercy:"ј",Jukcy:"Є",jukcy:"є",Kappa:"Κ",kappa:"κ",kappav:"ϰ",Kcedil:"Ķ",kcedil:"ķ",Kcy:"К",kcy:"к",Kfr:"𝔎",kfr:"𝔨",kgreen:"ĸ",KHcy:"Х",khcy:"х",KJcy:"Ќ",kjcy:"ќ",Kopf:"𝕂",kopf:"𝕜",Kscr:"𝒦",kscr:"𝓀",lAarr:"⇚",Lacute:"Ĺ",lacute:"ĺ",laemptyv:"⦴",lagran:"ℒ",Lambda:"Λ",lambda:"λ",Lang:"⟪",lang:"⟨",langd:"⦑",langle:"⟨",lap:"⪅",Laplacetrf:"ℒ",laquo:"«",Larr:"↞",lArr:"⇐",larr:"←",larrb:"⇤",larrbfs:"⤟",larrfs:"⤝",larrhk:"↩",larrlp:"↫",larrpl:"⤹",larrsim:"⥳",larrtl:"↢",lat:"⪫",lAtail:"⤛",latail:"⤙",late:"⪭",lates:"⪭︀",lBarr:"⤎",lbarr:"⤌",lbbrk:"❲",lbrace:"{",lbrack:"[",lbrke:"⦋",lbrksld:"⦏",lbrkslu:"⦍",Lcaron:"Ľ",lcaron:"ľ",Lcedil:"Ļ",lcedil:"ļ",lceil:"⌈",lcub:"{",Lcy:"Л",lcy:"л",ldca:"⤶",ldquo:"“",ldquor:"„",ldrdhar:"⥧",ldrushar:"⥋",ldsh:"↲",lE:"≦",le:"≤",LeftAngleBracket:"⟨",LeftArrow:"←",Leftarrow:"⇐",leftarrow:"←",LeftArrowBar:"⇤",LeftArrowRightArrow:"⇆",leftarrowtail:"↢",LeftCeiling:"⌈",LeftDoubleBracket:"⟦",LeftDownTeeVector:"⥡",LeftDownVector:"⇃",LeftDownVectorBar:"⥙",LeftFloor:"⌊",leftharpoondown:"↽",leftharpoonup:"↼",leftleftarrows:"⇇",LeftRightArrow:"↔",Leftrightarrow:"⇔",leftrightarrow:"↔",leftrightarrows:"⇆",leftrightharpoons:"⇋",leftrightsquigarrow:"↭",LeftRightVector:"⥎",LeftTee:"⊣",LeftTeeArrow:"↤",LeftTeeVector:"⥚",leftthreetimes:"⋋",LeftTriangle:"⊲",LeftTriangleBar:"⧏",LeftTriangleEqual:"⊴",LeftUpDownVector:"⥑",LeftUpTeeVector:"⥠",LeftUpVector:"↿",LeftUpVectorBar:"⥘",LeftVector:"↼",LeftVectorBar:"⥒",lEg:"⪋",leg:"⋚",leq:"≤",leqq:"≦",leqslant:"⩽",les:"⩽",lescc:"⪨",lesdot:"⩿",lesdoto:"⪁",lesdotor:"⪃",lesg:"⋚︀",lesges:"⪓",lessapprox:"⪅",lessdot:"⋖",lesseqgtr:"⋚",lesseqqgtr:"⪋",LessEqualGreater:"⋚",LessFullEqual:"≦",LessGreater:"≶",lessgtr:"≶",LessLess:"⪡",lesssim:"≲",LessSlantEqual:"⩽",LessTilde:"≲",lfisht:"⥼",lfloor:"⌊",Lfr:"𝔏",lfr:"𝔩",lg:"≶",lgE:"⪑",lHar:"⥢",lhard:"↽",lharu:"↼",lharul:"⥪",lhblk:"▄",LJcy:"Љ",ljcy:"љ",Ll:"⋘",ll:"≪",llarr:"⇇",llcorner:"⌞",Lleftarrow:"⇚",llhard:"⥫",lltri:"◺",Lmidot:"Ŀ",lmidot:"ŀ",lmoust:"⎰",lmoustache:"⎰",lnap:"⪉",lnapprox:"⪉",lnE:"≨",lne:"⪇",lneq:"⪇",lneqq:"≨",lnsim:"⋦",loang:"⟬",loarr:"⇽",lobrk:"⟦",LongLeftArrow:"⟵",Longleftarrow:"⟸",longleftarrow:"⟵",LongLeftRightArrow:"⟷",Longleftrightarrow:"⟺",longleftrightarrow:"⟷",longmapsto:"⟼",LongRightArrow:"⟶",Longrightarrow:"⟹",longrightarrow:"⟶",looparrowleft:"↫",looparrowright:"↬",lopar:"⦅",Lopf:"𝕃",lopf:"𝕝",loplus:"⨭",lotimes:"⨴",lowast:"∗",lowbar:"_",LowerLeftArrow:"↙",LowerRightArrow:"↘",loz:"◊",lozenge:"◊",lozf:"⧫",lpar:"(",lparlt:"⦓",lrarr:"⇆",lrcorner:"⌟",lrhar:"⇋",lrhard:"⥭",lrm:"‎",lrtri:"⊿",lsaquo:"‹",Lscr:"ℒ",lscr:"𝓁",Lsh:"↰",lsh:"↰",lsim:"≲",lsime:"⪍",lsimg:"⪏",lsqb:"[",lsquo:"‘",lsquor:"‚",Lstrok:"Ł",lstrok:"ł",Lt:"≪",LT:"<",lt:"<",ltcc:"⪦",ltcir:"⩹",ltdot:"⋖",lthree:"⋋",ltimes:"⋉",ltlarr:"⥶",ltquest:"⩻",ltri:"◃",ltrie:"⊴",ltrif:"◂",ltrPar:"⦖",lurdshar:"⥊",luruhar:"⥦",lvertneqq:"≨︀",lvnE:"≨︀",macr:"¯",male:"♂",malt:"✠",maltese:"✠",Map:"⤅",map:"↦",mapsto:"↦",mapstodown:"↧",mapstoleft:"↤",mapstoup:"↥",marker:"▮",mcomma:"⨩",Mcy:"М",mcy:"м",mdash:"—",mDDot:"∺",measuredangle:"∡",MediumSpace:" ",Mellintrf:"ℳ",Mfr:"𝔐",mfr:"𝔪",mho:"℧",micro:"µ",mid:"∣",midast:"*",midcir:"⫰",middot:"·",minus:"−",minusb:"⊟",minusd:"∸",minusdu:"⨪",MinusPlus:"∓",mlcp:"⫛",mldr:"…",mnplus:"∓",models:"⊧",Mopf:"𝕄",mopf:"𝕞",mp:"∓",Mscr:"ℳ",mscr:"𝓂",mstpos:"∾",Mu:"Μ",mu:"μ",multimap:"⊸",mumap:"⊸",nabla:"∇",Nacute:"Ń",nacute:"ń",nang:"∠⃒",nap:"≉",napE:"⩰̸",napid:"≋̸",napos:"ʼn",napprox:"≉",natur:"♮",natural:"♮",naturals:"ℕ",nbsp:" ",nbump:"≎̸",nbumpe:"≏̸",ncap:"⩃",Ncaron:"Ň",ncaron:"ň",Ncedil:"Ņ",ncedil:"ņ",ncong:"≇",ncongdot:"⩭̸",ncup:"⩂",Ncy:"Н",ncy:"н",ndash:"–",ne:"≠",nearhk:"⤤",neArr:"⇗",nearr:"↗",nearrow:"↗",nedot:"≐̸",NegativeMediumSpace:"​",NegativeThickSpace:"​",NegativeThinSpace:"​",NegativeVeryThinSpace:"​",nequiv:"≢",nesear:"⤨",nesim:"≂̸",NestedGreaterGreater:"≫",NestedLessLess:"≪",NewLine:` +`,nexist:"∄",nexists:"∄",Nfr:"𝔑",nfr:"𝔫",ngE:"≧̸",nge:"≱",ngeq:"≱",ngeqq:"≧̸",ngeqslant:"⩾̸",nges:"⩾̸",nGg:"⋙̸",ngsim:"≵",nGt:"≫⃒",ngt:"≯",ngtr:"≯",nGtv:"≫̸",nhArr:"⇎",nharr:"↮",nhpar:"⫲",ni:"∋",nis:"⋼",nisd:"⋺",niv:"∋",NJcy:"Њ",njcy:"њ",nlArr:"⇍",nlarr:"↚",nldr:"‥",nlE:"≦̸",nle:"≰",nLeftarrow:"⇍",nleftarrow:"↚",nLeftrightarrow:"⇎",nleftrightarrow:"↮",nleq:"≰",nleqq:"≦̸",nleqslant:"⩽̸",nles:"⩽̸",nless:"≮",nLl:"⋘̸",nlsim:"≴",nLt:"≪⃒",nlt:"≮",nltri:"⋪",nltrie:"⋬",nLtv:"≪̸",nmid:"∤",NoBreak:"⁠",NonBreakingSpace:" ",Nopf:"ℕ",nopf:"𝕟",Not:"⫬",not:"¬",NotCongruent:"≢",NotCupCap:"≭",NotDoubleVerticalBar:"∦",NotElement:"∉",NotEqual:"≠",NotEqualTilde:"≂̸",NotExists:"∄",NotGreater:"≯",NotGreaterEqual:"≱",NotGreaterFullEqual:"≧̸",NotGreaterGreater:"≫̸",NotGreaterLess:"≹",NotGreaterSlantEqual:"⩾̸",NotGreaterTilde:"≵",NotHumpDownHump:"≎̸",NotHumpEqual:"≏̸",notin:"∉",notindot:"⋵̸",notinE:"⋹̸",notinva:"∉",notinvb:"⋷",notinvc:"⋶",NotLeftTriangle:"⋪",NotLeftTriangleBar:"⧏̸",NotLeftTriangleEqual:"⋬",NotLess:"≮",NotLessEqual:"≰",NotLessGreater:"≸",NotLessLess:"≪̸",NotLessSlantEqual:"⩽̸",NotLessTilde:"≴",NotNestedGreaterGreater:"⪢̸",NotNestedLessLess:"⪡̸",notni:"∌",notniva:"∌",notnivb:"⋾",notnivc:"⋽",NotPrecedes:"⊀",NotPrecedesEqual:"⪯̸",NotPrecedesSlantEqual:"⋠",NotReverseElement:"∌",NotRightTriangle:"⋫",NotRightTriangleBar:"⧐̸",NotRightTriangleEqual:"⋭",NotSquareSubset:"⊏̸",NotSquareSubsetEqual:"⋢",NotSquareSuperset:"⊐̸",NotSquareSupersetEqual:"⋣",NotSubset:"⊂⃒",NotSubsetEqual:"⊈",NotSucceeds:"⊁",NotSucceedsEqual:"⪰̸",NotSucceedsSlantEqual:"⋡",NotSucceedsTilde:"≿̸",NotSuperset:"⊃⃒",NotSupersetEqual:"⊉",NotTilde:"≁",NotTildeEqual:"≄",NotTildeFullEqual:"≇",NotTildeTilde:"≉",NotVerticalBar:"∤",npar:"∦",nparallel:"∦",nparsl:"⫽⃥",npart:"∂̸",npolint:"⨔",npr:"⊀",nprcue:"⋠",npre:"⪯̸",nprec:"⊀",npreceq:"⪯̸",nrArr:"⇏",nrarr:"↛",nrarrc:"⤳̸",nrarrw:"↝̸",nRightarrow:"⇏",nrightarrow:"↛",nrtri:"⋫",nrtrie:"⋭",nsc:"⊁",nsccue:"⋡",nsce:"⪰̸",Nscr:"𝒩",nscr:"𝓃",nshortmid:"∤",nshortparallel:"∦",nsim:"≁",nsime:"≄",nsimeq:"≄",nsmid:"∤",nspar:"∦",nsqsube:"⋢",nsqsupe:"⋣",nsub:"⊄",nsubE:"⫅̸",nsube:"⊈",nsubset:"⊂⃒",nsubseteq:"⊈",nsubseteqq:"⫅̸",nsucc:"⊁",nsucceq:"⪰̸",nsup:"⊅",nsupE:"⫆̸",nsupe:"⊉",nsupset:"⊃⃒",nsupseteq:"⊉",nsupseteqq:"⫆̸",ntgl:"≹",Ntilde:"Ñ",ntilde:"ñ",ntlg:"≸",ntriangleleft:"⋪",ntrianglelefteq:"⋬",ntriangleright:"⋫",ntrianglerighteq:"⋭",Nu:"Ν",nu:"ν",num:"#",numero:"№",numsp:" ",nvap:"≍⃒",nVDash:"⊯",nVdash:"⊮",nvDash:"⊭",nvdash:"⊬",nvge:"≥⃒",nvgt:">⃒",nvHarr:"⤄",nvinfin:"⧞",nvlArr:"⤂",nvle:"≤⃒",nvlt:"<⃒",nvltrie:"⊴⃒",nvrArr:"⤃",nvrtrie:"⊵⃒",nvsim:"∼⃒",nwarhk:"⤣",nwArr:"⇖",nwarr:"↖",nwarrow:"↖",nwnear:"⤧",Oacute:"Ó",oacute:"ó",oast:"⊛",ocir:"⊚",Ocirc:"Ô",ocirc:"ô",Ocy:"О",ocy:"о",odash:"⊝",Odblac:"Ő",odblac:"ő",odiv:"⨸",odot:"⊙",odsold:"⦼",OElig:"Œ",oelig:"œ",ofcir:"⦿",Ofr:"𝔒",ofr:"𝔬",ogon:"˛",Ograve:"Ò",ograve:"ò",ogt:"⧁",ohbar:"⦵",ohm:"Ω",oint:"∮",olarr:"↺",olcir:"⦾",olcross:"⦻",oline:"‾",olt:"⧀",Omacr:"Ō",omacr:"ō",Omega:"Ω",omega:"ω",Omicron:"Ο",omicron:"ο",omid:"⦶",ominus:"⊖",Oopf:"𝕆",oopf:"𝕠",opar:"⦷",OpenCurlyDoubleQuote:"“",OpenCurlyQuote:"‘",operp:"⦹",oplus:"⊕",Or:"⩔",or:"∨",orarr:"↻",ord:"⩝",order:"ℴ",orderof:"ℴ",ordf:"ª",ordm:"º",origof:"⊶",oror:"⩖",orslope:"⩗",orv:"⩛",oS:"Ⓢ",Oscr:"𝒪",oscr:"ℴ",Oslash:"Ø",oslash:"ø",osol:"⊘",Otilde:"Õ",otilde:"õ",Otimes:"⨷",otimes:"⊗",otimesas:"⨶",Ouml:"Ö",ouml:"ö",ovbar:"⌽",OverBar:"‾",OverBrace:"⏞",OverBracket:"⎴",OverParenthesis:"⏜",par:"∥",para:"¶",parallel:"∥",parsim:"⫳",parsl:"⫽",part:"∂",PartialD:"∂",Pcy:"П",pcy:"п",percnt:"%",period:".",permil:"‰",perp:"⊥",pertenk:"‱",Pfr:"𝔓",pfr:"𝔭",Phi:"Φ",phi:"φ",phiv:"ϕ",phmmat:"ℳ",phone:"☎",Pi:"Π",pi:"π",pitchfork:"⋔",piv:"ϖ",planck:"ℏ",planckh:"ℎ",plankv:"ℏ",plus:"+",plusacir:"⨣",plusb:"⊞",pluscir:"⨢",plusdo:"∔",plusdu:"⨥",pluse:"⩲",PlusMinus:"±",plusmn:"±",plussim:"⨦",plustwo:"⨧",pm:"±",Poincareplane:"ℌ",pointint:"⨕",Popf:"ℙ",popf:"𝕡",pound:"£",Pr:"⪻",pr:"≺",prap:"⪷",prcue:"≼",prE:"⪳",pre:"⪯",prec:"≺",precapprox:"⪷",preccurlyeq:"≼",Precedes:"≺",PrecedesEqual:"⪯",PrecedesSlantEqual:"≼",PrecedesTilde:"≾",preceq:"⪯",precnapprox:"⪹",precneqq:"⪵",precnsim:"⋨",precsim:"≾",Prime:"″",prime:"′",primes:"ℙ",prnap:"⪹",prnE:"⪵",prnsim:"⋨",prod:"∏",Product:"∏",profalar:"⌮",profline:"⌒",profsurf:"⌓",prop:"∝",Proportion:"∷",Proportional:"∝",propto:"∝",prsim:"≾",prurel:"⊰",Pscr:"𝒫",pscr:"𝓅",Psi:"Ψ",psi:"ψ",puncsp:" ",Qfr:"𝔔",qfr:"𝔮",qint:"⨌",Qopf:"ℚ",qopf:"𝕢",qprime:"⁗",Qscr:"𝒬",qscr:"𝓆",quaternions:"ℍ",quatint:"⨖",quest:"?",questeq:"≟",QUOT:'"',quot:'"',rAarr:"⇛",race:"∽̱",Racute:"Ŕ",racute:"ŕ",radic:"√",raemptyv:"⦳",Rang:"⟫",rang:"⟩",rangd:"⦒",range:"⦥",rangle:"⟩",raquo:"»",Rarr:"↠",rArr:"⇒",rarr:"→",rarrap:"⥵",rarrb:"⇥",rarrbfs:"⤠",rarrc:"⤳",rarrfs:"⤞",rarrhk:"↪",rarrlp:"↬",rarrpl:"⥅",rarrsim:"⥴",Rarrtl:"⤖",rarrtl:"↣",rarrw:"↝",rAtail:"⤜",ratail:"⤚",ratio:"∶",rationals:"ℚ",RBarr:"⤐",rBarr:"⤏",rbarr:"⤍",rbbrk:"❳",rbrace:"}",rbrack:"]",rbrke:"⦌",rbrksld:"⦎",rbrkslu:"⦐",Rcaron:"Ř",rcaron:"ř",Rcedil:"Ŗ",rcedil:"ŗ",rceil:"⌉",rcub:"}",Rcy:"Р",rcy:"р",rdca:"⤷",rdldhar:"⥩",rdquo:"”",rdquor:"”",rdsh:"↳",Re:"ℜ",real:"ℜ",realine:"ℛ",realpart:"ℜ",reals:"ℝ",rect:"▭",REG:"®",reg:"®",ReverseElement:"∋",ReverseEquilibrium:"⇋",ReverseUpEquilibrium:"⥯",rfisht:"⥽",rfloor:"⌋",Rfr:"ℜ",rfr:"𝔯",rHar:"⥤",rhard:"⇁",rharu:"⇀",rharul:"⥬",Rho:"Ρ",rho:"ρ",rhov:"ϱ",RightAngleBracket:"⟩",RightArrow:"→",Rightarrow:"⇒",rightarrow:"→",RightArrowBar:"⇥",RightArrowLeftArrow:"⇄",rightarrowtail:"↣",RightCeiling:"⌉",RightDoubleBracket:"⟧",RightDownTeeVector:"⥝",RightDownVector:"⇂",RightDownVectorBar:"⥕",RightFloor:"⌋",rightharpoondown:"⇁",rightharpoonup:"⇀",rightleftarrows:"⇄",rightleftharpoons:"⇌",rightrightarrows:"⇉",rightsquigarrow:"↝",RightTee:"⊢",RightTeeArrow:"↦",RightTeeVector:"⥛",rightthreetimes:"⋌",RightTriangle:"⊳",RightTriangleBar:"⧐",RightTriangleEqual:"⊵",RightUpDownVector:"⥏",RightUpTeeVector:"⥜",RightUpVector:"↾",RightUpVectorBar:"⥔",RightVector:"⇀",RightVectorBar:"⥓",ring:"˚",risingdotseq:"≓",rlarr:"⇄",rlhar:"⇌",rlm:"‏",rmoust:"⎱",rmoustache:"⎱",rnmid:"⫮",roang:"⟭",roarr:"⇾",robrk:"⟧",ropar:"⦆",Ropf:"ℝ",ropf:"𝕣",roplus:"⨮",rotimes:"⨵",RoundImplies:"⥰",rpar:")",rpargt:"⦔",rppolint:"⨒",rrarr:"⇉",Rrightarrow:"⇛",rsaquo:"›",Rscr:"ℛ",rscr:"𝓇",Rsh:"↱",rsh:"↱",rsqb:"]",rsquo:"’",rsquor:"’",rthree:"⋌",rtimes:"⋊",rtri:"▹",rtrie:"⊵",rtrif:"▸",rtriltri:"⧎",RuleDelayed:"⧴",ruluhar:"⥨",rx:"℞",Sacute:"Ś",sacute:"ś",sbquo:"‚",Sc:"⪼",sc:"≻",scap:"⪸",Scaron:"Š",scaron:"š",sccue:"≽",scE:"⪴",sce:"⪰",Scedil:"Ş",scedil:"ş",Scirc:"Ŝ",scirc:"ŝ",scnap:"⪺",scnE:"⪶",scnsim:"⋩",scpolint:"⨓",scsim:"≿",Scy:"С",scy:"с",sdot:"⋅",sdotb:"⊡",sdote:"⩦",searhk:"⤥",seArr:"⇘",searr:"↘",searrow:"↘",sect:"§",semi:";",seswar:"⤩",setminus:"∖",setmn:"∖",sext:"✶",Sfr:"𝔖",sfr:"𝔰",sfrown:"⌢",sharp:"♯",SHCHcy:"Щ",shchcy:"щ",SHcy:"Ш",shcy:"ш",ShortDownArrow:"↓",ShortLeftArrow:"←",shortmid:"∣",shortparallel:"∥",ShortRightArrow:"→",ShortUpArrow:"↑",shy:"­",Sigma:"Σ",sigma:"σ",sigmaf:"ς",sigmav:"ς",sim:"∼",simdot:"⩪",sime:"≃",simeq:"≃",simg:"⪞",simgE:"⪠",siml:"⪝",simlE:"⪟",simne:"≆",simplus:"⨤",simrarr:"⥲",slarr:"←",SmallCircle:"∘",smallsetminus:"∖",smashp:"⨳",smeparsl:"⧤",smid:"∣",smile:"⌣",smt:"⪪",smte:"⪬",smtes:"⪬︀",SOFTcy:"Ь",softcy:"ь",sol:"/",solb:"⧄",solbar:"⌿",Sopf:"𝕊",sopf:"𝕤",spades:"♠",spadesuit:"♠",spar:"∥",sqcap:"⊓",sqcaps:"⊓︀",sqcup:"⊔",sqcups:"⊔︀",Sqrt:"√",sqsub:"⊏",sqsube:"⊑",sqsubset:"⊏",sqsubseteq:"⊑",sqsup:"⊐",sqsupe:"⊒",sqsupset:"⊐",sqsupseteq:"⊒",squ:"□",Square:"□",square:"□",SquareIntersection:"⊓",SquareSubset:"⊏",SquareSubsetEqual:"⊑",SquareSuperset:"⊐",SquareSupersetEqual:"⊒",SquareUnion:"⊔",squarf:"▪",squf:"▪",srarr:"→",Sscr:"𝒮",sscr:"𝓈",ssetmn:"∖",ssmile:"⌣",sstarf:"⋆",Star:"⋆",star:"☆",starf:"★",straightepsilon:"ϵ",straightphi:"ϕ",strns:"¯",Sub:"⋐",sub:"⊂",subdot:"⪽",subE:"⫅",sube:"⊆",subedot:"⫃",submult:"⫁",subnE:"⫋",subne:"⊊",subplus:"⪿",subrarr:"⥹",Subset:"⋐",subset:"⊂",subseteq:"⊆",subseteqq:"⫅",SubsetEqual:"⊆",subsetneq:"⊊",subsetneqq:"⫋",subsim:"⫇",subsub:"⫕",subsup:"⫓",succ:"≻",succapprox:"⪸",succcurlyeq:"≽",Succeeds:"≻",SucceedsEqual:"⪰",SucceedsSlantEqual:"≽",SucceedsTilde:"≿",succeq:"⪰",succnapprox:"⪺",succneqq:"⪶",succnsim:"⋩",succsim:"≿",SuchThat:"∋",Sum:"∑",sum:"∑",sung:"♪",Sup:"⋑",sup:"⊃",sup1:"¹",sup2:"²",sup3:"³",supdot:"⪾",supdsub:"⫘",supE:"⫆",supe:"⊇",supedot:"⫄",Superset:"⊃",SupersetEqual:"⊇",suphsol:"⟉",suphsub:"⫗",suplarr:"⥻",supmult:"⫂",supnE:"⫌",supne:"⊋",supplus:"⫀",Supset:"⋑",supset:"⊃",supseteq:"⊇",supseteqq:"⫆",supsetneq:"⊋",supsetneqq:"⫌",supsim:"⫈",supsub:"⫔",supsup:"⫖",swarhk:"⤦",swArr:"⇙",swarr:"↙",swarrow:"↙",swnwar:"⤪",szlig:"ß",Tab:" ",target:"⌖",Tau:"Τ",tau:"τ",tbrk:"⎴",Tcaron:"Ť",tcaron:"ť",Tcedil:"Ţ",tcedil:"ţ",Tcy:"Т",tcy:"т",tdot:"⃛",telrec:"⌕",Tfr:"𝔗",tfr:"𝔱",there4:"∴",Therefore:"∴",therefore:"∴",Theta:"Θ",theta:"θ",thetasym:"ϑ",thetav:"ϑ",thickapprox:"≈",thicksim:"∼",ThickSpace:"  ",thinsp:" ",ThinSpace:" ",thkap:"≈",thksim:"∼",THORN:"Þ",thorn:"þ",Tilde:"∼",tilde:"˜",TildeEqual:"≃",TildeFullEqual:"≅",TildeTilde:"≈",times:"×",timesb:"⊠",timesbar:"⨱",timesd:"⨰",tint:"∭",toea:"⤨",top:"⊤",topbot:"⌶",topcir:"⫱",Topf:"𝕋",topf:"𝕥",topfork:"⫚",tosa:"⤩",tprime:"‴",TRADE:"™",trade:"™",triangle:"▵",triangledown:"▿",triangleleft:"◃",trianglelefteq:"⊴",triangleq:"≜",triangleright:"▹",trianglerighteq:"⊵",tridot:"◬",trie:"≜",triminus:"⨺",TripleDot:"⃛",triplus:"⨹",trisb:"⧍",tritime:"⨻",trpezium:"⏢",Tscr:"𝒯",tscr:"𝓉",TScy:"Ц",tscy:"ц",TSHcy:"Ћ",tshcy:"ћ",Tstrok:"Ŧ",tstrok:"ŧ",twixt:"≬",twoheadleftarrow:"↞",twoheadrightarrow:"↠",Uacute:"Ú",uacute:"ú",Uarr:"↟",uArr:"⇑",uarr:"↑",Uarrocir:"⥉",Ubrcy:"Ў",ubrcy:"ў",Ubreve:"Ŭ",ubreve:"ŭ",Ucirc:"Û",ucirc:"û",Ucy:"У",ucy:"у",udarr:"⇅",Udblac:"Ű",udblac:"ű",udhar:"⥮",ufisht:"⥾",Ufr:"𝔘",ufr:"𝔲",Ugrave:"Ù",ugrave:"ù",uHar:"⥣",uharl:"↿",uharr:"↾",uhblk:"▀",ulcorn:"⌜",ulcorner:"⌜",ulcrop:"⌏",ultri:"◸",Umacr:"Ū",umacr:"ū",uml:"¨",UnderBar:"_",UnderBrace:"⏟",UnderBracket:"⎵",UnderParenthesis:"⏝",Union:"⋃",UnionPlus:"⊎",Uogon:"Ų",uogon:"ų",Uopf:"𝕌",uopf:"𝕦",UpArrow:"↑",Uparrow:"⇑",uparrow:"↑",UpArrowBar:"⤒",UpArrowDownArrow:"⇅",UpDownArrow:"↕",Updownarrow:"⇕",updownarrow:"↕",UpEquilibrium:"⥮",upharpoonleft:"↿",upharpoonright:"↾",uplus:"⊎",UpperLeftArrow:"↖",UpperRightArrow:"↗",Upsi:"ϒ",upsi:"υ",upsih:"ϒ",Upsilon:"Υ",upsilon:"υ",UpTee:"⊥",UpTeeArrow:"↥",upuparrows:"⇈",urcorn:"⌝",urcorner:"⌝",urcrop:"⌎",Uring:"Ů",uring:"ů",urtri:"◹",Uscr:"𝒰",uscr:"𝓊",utdot:"⋰",Utilde:"Ũ",utilde:"ũ",utri:"▵",utrif:"▴",uuarr:"⇈",Uuml:"Ü",uuml:"ü",uwangle:"⦧",vangrt:"⦜",varepsilon:"ϵ",varkappa:"ϰ",varnothing:"∅",varphi:"ϕ",varpi:"ϖ",varpropto:"∝",vArr:"⇕",varr:"↕",varrho:"ϱ",varsigma:"ς",varsubsetneq:"⊊︀",varsubsetneqq:"⫋︀",varsupsetneq:"⊋︀",varsupsetneqq:"⫌︀",vartheta:"ϑ",vartriangleleft:"⊲",vartriangleright:"⊳",Vbar:"⫫",vBar:"⫨",vBarv:"⫩",Vcy:"В",vcy:"в",VDash:"⊫",Vdash:"⊩",vDash:"⊨",vdash:"⊢",Vdashl:"⫦",Vee:"⋁",vee:"∨",veebar:"⊻",veeeq:"≚",vellip:"⋮",Verbar:"‖",verbar:"|",Vert:"‖",vert:"|",VerticalBar:"∣",VerticalLine:"|",VerticalSeparator:"❘",VerticalTilde:"≀",VeryThinSpace:" ",Vfr:"𝔙",vfr:"𝔳",vltri:"⊲",vnsub:"⊂⃒",vnsup:"⊃⃒",Vopf:"𝕍",vopf:"𝕧",vprop:"∝",vrtri:"⊳",Vscr:"𝒱",vscr:"𝓋",vsubnE:"⫋︀",vsubne:"⊊︀",vsupnE:"⫌︀",vsupne:"⊋︀",Vvdash:"⊪",vzigzag:"⦚",Wcirc:"Ŵ",wcirc:"ŵ",wedbar:"⩟",Wedge:"⋀",wedge:"∧",wedgeq:"≙",weierp:"℘",Wfr:"𝔚",wfr:"𝔴",Wopf:"𝕎",wopf:"𝕨",wp:"℘",wr:"≀",wreath:"≀",Wscr:"𝒲",wscr:"𝓌",xcap:"⋂",xcirc:"◯",xcup:"⋃",xdtri:"▽",Xfr:"𝔛",xfr:"𝔵",xhArr:"⟺",xharr:"⟷",Xi:"Ξ",xi:"ξ",xlArr:"⟸",xlarr:"⟵",xmap:"⟼",xnis:"⋻",xodot:"⨀",Xopf:"𝕏",xopf:"𝕩",xoplus:"⨁",xotime:"⨂",xrArr:"⟹",xrarr:"⟶",Xscr:"𝒳",xscr:"𝓍",xsqcup:"⨆",xuplus:"⨄",xutri:"△",xvee:"⋁",xwedge:"⋀",Yacute:"Ý",yacute:"ý",YAcy:"Я",yacy:"я",Ycirc:"Ŷ",ycirc:"ŷ",Ycy:"Ы",ycy:"ы",yen:"¥",Yfr:"𝔜",yfr:"𝔶",YIcy:"Ї",yicy:"ї",Yopf:"𝕐",yopf:"𝕪",Yscr:"𝒴",yscr:"𝓎",YUcy:"Ю",yucy:"ю",Yuml:"Ÿ",yuml:"ÿ",Zacute:"Ź",zacute:"ź",Zcaron:"Ž",zcaron:"ž",Zcy:"З",zcy:"з",Zdot:"Ż",zdot:"ż",zeetrf:"ℨ",ZeroWidthSpace:"​",Zeta:"Ζ",zeta:"ζ",Zfr:"ℨ",zfr:"𝔷",ZHcy:"Ж",zhcy:"ж",zigrarr:"⇝",Zopf:"ℤ",zopf:"𝕫",Zscr:"𝒵",zscr:"𝓏",zwj:"‍",zwnj:"‌"}),y.entityMap=y.HTML_ENTITIES})(Gi)),Gi}var si={},sn;function Ks(){if(sn)return si;sn=1;var y=bi().NAMESPACE,e=/[A-Z_a-z\xC0-\xD6\xD8-\xF6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]/,t=new RegExp("[\\-\\.0-9"+e.source.slice(1,-1)+"\\u00B7\\u0300-\\u036F\\u203F-\\u2040]"),i=new RegExp("^"+e.source+t.source+"*(?::"+e.source+t.source+"*)?$"),r=0,n=1,s=2,a=3,u=4,h=5,d=6,c=7;function w(k,O){this.message=k,this.locator=O,Error.captureStackTrace&&Error.captureStackTrace(this,w)}w.prototype=new Error,w.prototype.name=w.name;function m(){}m.prototype={parse:function(k,O,G){var j=this.domBuilder;j.startDocument(),L(O,O={}),N(k,O,G,j,this.errorHandler),j.endDocument()}};function N(k,O,G,j,Z){function Y(Ee){if(Ee>65535){Ee-=65536;var De=55296+(Ee>>10),_e=56320+(Ee&1023);return String.fromCharCode(De,_e)}else return String.fromCharCode(Ee)}function X(Ee){var De=Ee.slice(1,-1);return De in G?G[De]:De.charAt(0)==="#"?Y(parseInt(De.substr(1).replace("x","0x"))):(Z.error("entity not found:"+Ee),Ee)}function ie(Ee){if(Ee>ye){var De=k.substring(ye,Ee).replace(/&#?\w+;/g,X);ne&&K(ye),j.characters(De,0,Ee-ye),ye=Ee}}function K(Ee,De){for(;Ee>=ae&&(De=pe.exec(k));)$=De.index,ae=$+De[0].length,ne.lineNumber++;ne.columnNumber=Ee-$+1}for(var $=0,ae=0,pe=/.*(?:\r\n?|\n)|.*$/g,ne=j.locator,ce=[{currentNSMap:O}],Ce={},ye=0;;){try{var Te=k.indexOf("<",ye);if(Te<0){if(!k.substr(ye).match(/^\s*$/)){var We=j.doc,Fe=We.createTextNode(k.substr(ye));We.appendChild(Fe),j.currentElement=Fe}return}switch(Te>ye&&ie(Te),k.charAt(Te+1)){case"/":var ze=k.indexOf(">",Te+3),be=k.substring(Te+2,ze).replace(/[ \t\n\r]+$/g,""),Pe=ce.pop();ze<0?(be=k.substring(Te+2).replace(/[\s<].*/,""),Z.error("end tag name: "+be+" is not complete:"+Pe.tagName),ze=Te+1+be.length):be.match(/\sye?ye=ze:ie(Math.max(Te,ye)+1)}}function E(k,O){return O.lineNumber=k.lineNumber,O.columnNumber=k.columnNumber,O}function f(k,O,G,j,Z,Y){function X(ne,ce,Ce){G.attributeNames.hasOwnProperty(ne)&&Y.fatalError("Attribute "+ne+" redefined"),G.addValue(ne,ce,Ce)}for(var ie,K,$=++O,ae=r;;){var pe=k.charAt($);switch(pe){case"=":if(ae===n)ie=k.slice(O,$),ae=a;else if(ae===s)ae=a;else throw new Error("attribute equal must after attrName");break;case"'":case'"':if(ae===a||ae===n)if(ae===n&&(Y.warning('attribute value must after "="'),ie=k.slice(O,$)),O=$+1,$=k.indexOf(pe,O),$>0)K=k.slice(O,$).replace(/&#?\w+;/g,Z),X(ie,K,O-1),ae=h;else throw new Error("attribute value no end '"+pe+"' match");else if(ae==u)K=k.slice(O,$).replace(/&#?\w+;/g,Z),X(ie,K,O),Y.warning('attribute "'+ie+'" missed start quot('+pe+")!!"),O=$+1,ae=h;else throw new Error('attribute value must after "="');break;case"/":switch(ae){case r:G.setTagName(k.slice(O,$));case h:case d:case c:ae=c,G.closed=!0;case u:case n:break;case s:G.closed=!0;break;default:throw new Error("attribute invalid close char('/')")}break;case"":return Y.error("unexpected end of input"),ae==r&&G.setTagName(k.slice(O,$)),$;case">":switch(ae){case r:G.setTagName(k.slice(O,$));case h:case d:case c:break;case u:case n:K=k.slice(O,$),K.slice(-1)==="/"&&(G.closed=!0,K=K.slice(0,-1));case s:ae===s&&(K=ie),ae==u?(Y.warning('attribute "'+K+'" missed quot(")!'),X(ie,K.replace(/&#?\w+;/g,Z),O)):((!y.isHTML(j[""])||!K.match(/^(?:disabled|checked|selected)$/i))&&Y.warning('attribute "'+K+'" missed value!! "'+K+'" instead!!'),X(K,K,O));break;case a:throw new Error("attribute value missed!!")}return $;case"€":pe=" ";default:if(pe<=" ")switch(ae){case r:G.setTagName(k.slice(O,$)),ae=d;break;case n:ie=k.slice(O,$),ae=s;break;case u:var K=k.slice(O,$).replace(/&#?\w+;/g,Z);Y.warning('attribute "'+K+'" missed quot(")!!'),X(ie,K,O);case h:ae=d;break}else switch(ae){case s:G.tagName,(!y.isHTML(j[""])||!ie.match(/^(?:disabled|checked|selected)$/i))&&Y.warning('attribute "'+ie+'" missed value!! "'+ie+'" instead2!!'),X(ie,ie,O),O=$,ae=n;break;case h:Y.warning('attribute space is required"'+ie+'"!!');case d:ae=n,O=$;break;case a:ae=u,O=$;break;case c:throw new Error("elements closed character '/' and '>' must be connected to")}}$++}}function v(k,O,G){for(var j=k.tagName,Z=null,pe=k.length;pe--;){var Y=k[pe],X=Y.qName,ie=Y.value,ne=X.indexOf(":");if(ne>0)var K=Y.prefix=X.slice(0,ne),$=X.slice(ne+1),ae=K==="xmlns"&&$;else $=X,K=null,ae=X==="xmlns"&&"";Y.localName=$,ae!==!1&&(Z==null&&(Z={},L(G,G={})),G[ae]=Z[ae]=ie,Y.uri=y.XMLNS,O.startPrefixMapping(ae,ie))}for(var pe=k.length;pe--;){Y=k[pe];var K=Y.prefix;K&&(K==="xml"&&(Y.uri=y.XML),K!=="xmlns"&&(Y.uri=G[K||""]))}var ne=j.indexOf(":");ne>0?(K=k.prefix=j.slice(0,ne),$=k.localName=j.slice(ne+1)):(K=null,$=k.localName=j);var ce=k.uri=G[K||""];if(O.startElement(ce,$,j,k),k.closed){if(O.endElement(ce,$,j),Z)for(K in Z)Object.prototype.hasOwnProperty.call(Z,K)&&O.endPrefixMapping(K)}else return k.currentNSMap=G,k.localNSMap=Z,!0}function g(k,O,G,j,Z){if(/^(?:script|textarea)$/i.test(G)){var Y=k.indexOf("",O),X=k.substring(O+1,Y);if(/[&<]/.test(X))return/^script$/i.test(G)?(Z.characters(X,0,X.length),Y):(X=X.replace(/&#?\w+;/g,j),Z.characters(X,0,X.length),Y)}return O+1}function D(k,O,G,j){var Z=j[G];return Z==null&&(Z=k.lastIndexOf(""),Z",O+4);return Y>O?(G.comment(k,O+4,Y-O-4),Y+3):(j.error("Unclosed comment"),-1)}else return-1;default:if(k.substr(O+3,6)=="CDATA["){var Y=k.indexOf("]]>",O+9);return G.startCDATA(),G.characters(k,O+9,Y-O-9),G.endCDATA(),Y+3}var X=I(k,O),ie=X.length;if(ie>1&&/!doctype/i.test(X[0][0])){var K=X[1][0],$=!1,ae=!1;ie>3&&(/^public$/i.test(X[2][0])?($=X[3][0],ae=ie>4&&X[4][0]):/^system$/i.test(X[2][0])&&(ae=X[3][0]));var pe=X[ie-1];return G.startDTD(K,$,ae),G.endDTD(),pe.index+pe[0].length}}return-1}function S(k,O,G){var j=k.indexOf("?>",O);if(j){var Z=k.substring(O,j).match(/^<\?(\S*)\s*([\s\S]*?)\s*$/);return Z?(Z[0].length,G.processingInstruction(Z[1],Z[2]),j+2):-1}return-1}function R(){this.attributeNames={}}R.prototype={setTagName:function(k){if(!i.test(k))throw new Error("invalid tagName:"+k);this.tagName=k},addValue:function(k,O,G){if(!i.test(k))throw new Error("invalid attribute:"+k);this.attributeNames[k]=this.length,this[this.length++]={qName:k,value:O,offset:G}},length:0,getLocalName:function(k){return this[k].localName},getLocator:function(k){return this[k].locator},getQName:function(k){return this[k].qName},getURI:function(k){return this[k].uri},getValue:function(k){return this[k].value}};function I(k,O){var G,j=[],Z=/'[^']+'|"[^"]+"|[^\s<>\/=]+=?|(\/?\s*>|<)/g;for(Z.lastIndex=O,Z.exec(k);G=Z.exec(k);)if(j.push(G),G[1])return j}return si.XMLReader=m,si.ParseError=w,si}var an;function Js(){if(an)return Mt;an=1;var y=bi(),e=Hn(),t=$s(),i=Ks(),r=e.DOMImplementation,n=y.NAMESPACE,s=i.ParseError,a=i.XMLReader;function u(E){this.options=E||{locator:{}}}u.prototype.parseFromString=function(E,f){var v=this.options,g=new a,D=v.domBuilder||new d,L=v.errorHandler,b=v.locator,S=v.xmlns||{},R=/\/x?html?$/.test(f),I=R?t.HTML_ENTITIES:t.XML_ENTITIES;return b&&D.setDocumentLocator(b),g.errorHandler=h(L,D,b),g.domBuilder=v.domBuilder||D,R&&(S[""]=n.HTML),S.xml=S.xml||n.XML,E&&typeof E=="string"?g.parse(E,S,I):g.errorHandler.error("invalid doc source"),D.doc};function h(E,f,v){if(!E){if(f instanceof d)return f;E=f}var g={},D=E instanceof Function;v=v||{};function L(b){var S=E[b];!S&&D&&(S=E.length==2?function(R){E(b,R)}:E),g[b]=S&&function(R){S("[xmldom "+b+"] "+R+w(v))}||function(){}}return L("warning"),L("error"),L("fatalError"),g}function d(){this.cdata=!1}function c(E,f){f.lineNumber=E.lineNumber,f.columnNumber=E.columnNumber}d.prototype={startDocument:function(){this.doc=new r().createDocument(null,null,null),this.locator&&(this.doc.documentURI=this.locator.systemId)},startElement:function(E,f,v,g){var D=this.doc,L=D.createElementNS(E,v||f),b=g.length;N(this,L),this.currentElement=L,this.locator&&c(this.locator,L);for(var S=0;S=f+v||f?new java.lang.String(E,f,v)+"":E}"endDTD,startEntity,endEntity,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,resolveEntity,getExternalSubset,notationDecl,unparsedEntityDecl".replace(/\w+/g,function(E){d.prototype[E]=function(){return null}});function N(E,f){E.currentElement?E.currentElement.appendChild(f):E.doc.appendChild(f)}return Mt.__DOMHandler=d,Mt.DOMParser=u,Mt.DOMImplementation=e.DOMImplementation,Mt.XMLSerializer=e.XMLSerializer,Mt}var on;function Qs(){if(on)return Kt;on=1;var y=Hn();return Kt.DOMImplementation=y.DOMImplementation,Kt.XMLSerializer=y.XMLSerializer,Kt.DOMParser=Js().DOMParser,Kt}var Vn=Qs();const xr=typeof window<"u"?window.requestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||window.msRequestAnimationFrame:!1,ea=1,ta=3,Gn=typeof URL<"u"?URL:typeof window<"u"?window.URL||window.webkitURL||window.mozURL:void 0;function wi(){var y=new Date().getTime(),e="xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(t){var i=(y+Math.random()*16)%16|0;return y=Math.floor(y/16),(t=="x"?i:i&7|8).toString(16)});return e}function ia(){return Math.max(document.documentElement.clientHeight,document.body.scrollHeight,document.documentElement.scrollHeight,document.body.offsetHeight,document.documentElement.offsetHeight)}function Zn(y){return!!(y&&y.nodeType==1)}function Ke(y){return!isNaN(parseFloat(y))&&isFinite(y)}function Yn(y){let e=parseFloat(y);return Ke(y)===!1?!1:typeof y=="string"&&y.indexOf(".")>-1?!0:Math.floor(e)!==e}function jt(y){var e=["Webkit","webkit","Moz","O","ms"],t=["-webkit-","-webkit-","-moz-","-o-","-ms-"],i=y.toLowerCase(),r=e.length;if(typeof document>"u"||typeof document.body.style[i]<"u")return y;for(var n=0;nd)return 1;if(h=0?a:a+1:u===0?a:u===-1?Zt(y,e,t,a,s):Zt(y,e,t,n,a))}function hi(y,e,t,i,r){var n=i||0,s=r||e.length,a=parseInt(n+(s-n)/2),u;return t||(t=function(h,d){if(h>d)return 1;if(h-1}function Kn(y,e){return new Blob([y],{type:e})}function di(y,e){var t,i=Kn(y,e);return t=Gn.createObjectURL(i),t}function Jn(y){return Gn.revokeObjectURL(y)}function vr(y,e){var t,i;if(typeof y=="string")return t=btoa(y),i="data:"+e+";base64,"+t,i}function Qn(y){return Object.prototype.toString.call(y).slice(8,-1)}function Ct(y,e,t){var i,r;return typeof DOMParser>"u"||t?r=Vn.DOMParser:r=DOMParser,y.charCodeAt(0)===65279&&(y=y.slice(1)),i=new r().parseFromString(y,e),i}function Ve(y,e){var t;if(!y)throw new Error("No Element Provided");if(typeof y.querySelector<"u")return y.querySelector(e);if(t=y.getElementsByTagName(e),t.length)return t[0]}function Rt(y,e){return typeof y.querySelector<"u"?y.querySelectorAll(e):y.getElementsByTagName(e)}function Wt(y,e,t){var i,r;if(typeof y.querySelector<"u"){e+="[";for(var n in t)e+=n+"~='"+t[n]+"'";return e+="]",y.querySelector(e)}else if(i=y.getElementsByTagName(e),r=Array.prototype.slice.call(i,0).filter(function(s){for(var a in t)if(s.getAttribute(a)===t[a])return!0;return!1}),r)return r[0]}function pi(y,e){var t=y.ownerDocument||y;typeof t.createTreeWalker<"u"?es(y,e,NodeFilter.SHOW_TEXT):Dr(y,function(i){i&&i.nodeType===3&&e(i)})}function es(y,e,t){var i=document.createTreeWalker(y,t,null,!1);let r;for(;r=i.nextNode();)e(r)}function Dr(y,e){if(e(y))return!0;if(y=y.firstChild,y)do{if(Dr(y,e))return!0;y=y.nextSibling}while(y)}function ts(y){return new Promise(function(e,t){var i=new FileReader;i.readAsDataURL(y),i.onloadend=function(){e(i.result)}})}function Le(){this.resolve=null,this.reject=null,this.id=wi(),this.promise=new Promise((y,e)=>{this.resolve=y,this.reject=e}),Object.freeze(this)}function vi(y,e,t){var i;if(typeof y.querySelector<"u"&&(i=y.querySelector(`${e}[*|type="${t}"]`)),!i||i.length===0){i=Rt(y,e);for(var r=0;r2){for(var w=a.length-1,m=w;m>=0&&a.charCodeAt(m)!==47;--m);if(m!==w){m===-1?a="":a=a.slice(0,m),u=c,h=0;continue}}else if(a.length===2||a.length===1){a="",u=c,h=0;continue}}s&&(a.length>0?a+="/..":a="..")}else a.length>0?a+="/"+n.slice(u+1,c):a=n.slice(u+1,c);u=c,h=0}else d===46&&h!==-1?++h:h=-1}return a}function i(n,s){var a=s.dir||s.root,u=s.base||(s.name||"")+(s.ext||"");return a?a===s.root?a+u:a+n+u:u}var r={resolve:function(){for(var s="",a=!1,u,h=arguments.length-1;h>=-1&&!a;h--){var d;h>=0?d=arguments[h]:(u===void 0&&(u=y.cwd()),d=u),e(d),d.length!==0&&(s=d+"/"+s,a=d.charCodeAt(0)===47)}return s=t(s,!a),a?s.length>0?"/"+s:"/":s.length>0?s:"."},normalize:function(s){if(e(s),s.length===0)return".";var a=s.charCodeAt(0)===47,u=s.charCodeAt(s.length-1)===47;return s=t(s,!a),s.length===0&&!a&&(s="."),s.length>0&&u&&(s+="/"),a?"/"+s:s},isAbsolute:function(s){return e(s),s.length>0&&s.charCodeAt(0)===47},join:function(){if(arguments.length===0)return".";for(var s,a=0;a0&&(s===void 0?s=u:s+="/"+u)}return s===void 0?".":r.normalize(s)},relative:function(s,a){if(e(s),e(a),s===a||(s=r.resolve(s),a=r.resolve(a),s===a))return"";for(var u=1;uN){if(a.charCodeAt(c+f)===47)return a.slice(c+f+1);if(f===0)return a.slice(c+f)}else d>N&&(s.charCodeAt(u+f)===47?E=f:f===0&&(E=0));break}var v=s.charCodeAt(u+f),g=a.charCodeAt(c+f);if(v!==g)break;v===47&&(E=f)}var D="";for(f=u+E+1;f<=h;++f)(f===h||s.charCodeAt(f)===47)&&(D.length===0?D+="..":D+="/..");return D.length>0?D+a.slice(c+E):(c+=E,a.charCodeAt(c)===47&&++c,a.slice(c))},_makeLong:function(s){return s},dirname:function(s){if(e(s),s.length===0)return".";for(var a=s.charCodeAt(0),u=a===47,h=-1,d=!0,c=s.length-1;c>=1;--c)if(a=s.charCodeAt(c),a===47){if(!d){h=c;break}}else d=!1;return h===-1?u?"/":".":u&&h===1?"//":s.slice(0,h)},basename:function(s,a){if(a!==void 0&&typeof a!="string")throw new TypeError('"ext" argument must be a string');e(s);var u=0,h=-1,d=!0,c;if(a!==void 0&&a.length>0&&a.length<=s.length){if(a.length===s.length&&a===s)return"";var w=a.length-1,m=-1;for(c=s.length-1;c>=0;--c){var N=s.charCodeAt(c);if(N===47){if(!d){u=c+1;break}}else m===-1&&(d=!1,m=c+1),w>=0&&(N===a.charCodeAt(w)?--w===-1&&(h=c):(w=-1,h=m))}return u===h?h=m:h===-1&&(h=s.length),s.slice(u,h)}else{for(c=s.length-1;c>=0;--c)if(s.charCodeAt(c)===47){if(!d){u=c+1;break}}else h===-1&&(d=!1,h=c+1);return h===-1?"":s.slice(u,h)}},extname:function(s){e(s);for(var a=-1,u=0,h=-1,d=!0,c=0,w=s.length-1;w>=0;--w){var m=s.charCodeAt(w);if(m===47){if(!d){u=w+1;break}continue}h===-1&&(d=!1,h=w+1),m===46?a===-1?a=w:c!==1&&(c=1):a!==-1&&(c=-1)}return a===-1||h===-1||c===0||c===1&&a===h-1&&a===u+1?"":s.slice(a,h)},format:function(s){if(s===null||typeof s!="object")throw new TypeError('Parameter "pathObject" must be an object, not '+typeof s);return i("/",s)},parse:function(s){e(s);var a={root:"",dir:"",base:"",ext:"",name:""};if(s.length===0)return a;var u=s.charCodeAt(0),h=u===47,d;h?(a.root="/",d=1):d=0;for(var c=-1,w=0,m=-1,N=!0,E=s.length-1,f=0;E>=d;--E){if(u=s.charCodeAt(E),u===47){if(!N){w=E+1;break}continue}m===-1&&(N=!1,m=E+1),u===46?c===-1?c=E:f!==1&&(f=1):c!==-1&&(f=-1)}return c===-1||m===-1||f===0||f===1&&c===m-1&&c===w+1?m!==-1&&(w===0&&h?a.base=a.name=s.slice(1,m):a.base=a.name=s.slice(w,m)):(w===0&&h?(a.name=s.slice(1,c),a.base=s.slice(1,m)):(a.name=s.slice(w,c),a.base=s.slice(w,m)),a.ext=s.slice(c,m)),w>0?a.dir=s.slice(0,w-1):h&&(a.dir="/"),a},sep:"/",delimiter:":",posix:null};return Zi=r,Zi}var ua=oa();const Ot=Yt(ua);class It{constructor(e){var t,i;t=e.indexOf("://"),t>-1&&(e=new URL(e).pathname),i=this.parse(e),this.path=e,this.isDirectory(e)?this.directory=e:this.directory=i.dir+"/",this.filename=i.base,this.extension=i.ext.slice(1)}parse(e){return Ot.parse(e)}isAbsolute(e){return Ot.isAbsolute(e||this.path)}isDirectory(e){return e.charAt(e.length-1)==="/"}resolve(e){return Ot.resolve(this.directory,e)}relative(e){var t=e&&e.indexOf("://")>-1;return t?e:Ot.relative(this.directory,e)}splitPath(e){return this.splitPathRe.exec(e).slice(1)}toString(){return this.path}}class At{constructor(e,t){var i=e.indexOf("://")>-1,r=e,n;if(this.Url=void 0,this.href=e,this.protocol="",this.origin="",this.hash="",this.hash="",this.search="",this.base=t,!i&&t!==!1&&typeof t!="string"&&window&&window.location&&(this.base=window.location.href),i||this.base)try{this.base?this.Url=new URL(e,this.base):this.Url=new URL(e),this.href=this.Url.href,this.protocol=this.Url.protocol,this.origin=this.Url.origin,this.hash=this.Url.hash,this.search=this.Url.search,r=this.Url.pathname+(this.Url.search?this.Url.search:"")}catch{this.Url=void 0,this.base&&(n=new It(this.base),r=n.resolve(r))}this.Path=new It(r),this.directory=this.Path.directory,this.filename=this.Path.filename,this.extension=this.Path.extension}path(){return this.Path}resolve(e){var t=e.indexOf("://")>-1,i;return t?e:(i=Ot.resolve(this.directory,e),this.origin+i)}relative(e){return Ot.relative(e,this.directory)}toString(){return this.href}}const Ut=1,yt=3,la=9;class Be{constructor(e,t,i){var r;if(this.str="",this.base={},this.spinePos=0,this.range=!1,this.path={},this.start=null,this.end=null,!(this instanceof Be))return new Be(e,t,i);if(typeof t=="string"?this.base=this.parseComponent(t):typeof t=="object"&&t.steps&&(this.base=t),r=this.checkType(e),r==="string")return this.str=e,ut(this,this.parse(e));if(r==="range")return ut(this,this.fromRange(e,this.base,i));if(r==="node")return ut(this,this.fromNode(e,this.base,i));if(r==="EpubCFI"&&e.path)return e;if(e)throw new TypeError("not a valid argument for EpubCFI");return this}checkType(e){return this.isCfiString(e)?"string":e&&typeof e=="object"&&(Qn(e)==="Range"||typeof e.startContainer<"u")?"range":e&&typeof e=="object"&&typeof e.nodeType<"u"?"node":e&&typeof e=="object"&&e instanceof Be?"EpubCFI":!1}parse(e){var t={spinePos:-1,range:!1,base:{},path:{},start:null,end:null},i,r,n;return typeof e!="string"?{spinePos:-1}:(e.indexOf("epubcfi(")===0&&e[e.length-1]===")"&&(e=e.slice(8,e.length-1)),i=this.getChapterComponent(e),i?(t.base=this.parseComponent(i),r=this.getPathComponent(e),t.path=this.parseComponent(r),n=this.getRange(e),n&&(t.range=!0,t.start=this.parseComponent(n[0]),t.end=this.parseComponent(n[1])),t.spinePos=t.base.steps[1].index,t):{spinePos:-1})}parseComponent(e){var t={steps:[],terminal:{offset:null,assertion:null}},i=e.split(":"),r=i[0].split("/"),n;return i.length>1&&(n=i[1],t.terminal=this.parseTerminal(n)),r[0]===""&&r.shift(),t.steps=r.map((function(s){return this.parseStep(s)}).bind(this)),t}parseStep(e){var t,i,r,n,s;if(n=e.match(/\[(.*)\]/),n&&n[1]&&(s=n[1]),i=parseInt(e),!isNaN(i))return i%2===0?(t="element",r=i/2-1):(t="text",r=(i-1)/2),{type:t,index:r,id:s||null}}parseTerminal(e){var t,i,r=e.match(/\[(.*)\]/);return r&&r[1]?(t=parseInt(e.split("[")[0]),i=r[1]):t=parseInt(e),Ke(t)||(t=null),{offset:t,assertion:i}}getChapterComponent(e){var t=e.split("!");return t[0]}getPathComponent(e){var t=e.split("!");if(t[1])return t[1].split(",")[0]}getRange(e){var t=e.split(",");return t.length===3?[t[1],t[2]]:!1}getCharecterOffsetComponent(e){var t=e.split(":");return t[1]||""}joinSteps(e){return e?e.map(function(t){var i="";return t.type==="element"&&(i+=(t.index+1)*2),t.type==="text"&&(i+=1+2*t.index),t.id&&(i+="["+t.id+"]"),i}).join("/"):""}segmentString(e){var t="/";return t+=this.joinSteps(e.steps),e.terminal&&e.terminal.offset!=null&&(t+=":"+e.terminal.offset),e.terminal&&e.terminal.assertion!=null&&(t+="["+e.terminal.assertion+"]"),t}toString(){var e="epubcfi(";return e+=this.segmentString(this.base),e+="!",e+=this.segmentString(this.path),this.range&&this.start&&(e+=",",e+=this.segmentString(this.start)),this.range&&this.end&&(e+=",",e+=this.segmentString(this.end)),e+=")",e}compare(e,t){var i,r,n,s;if(typeof e=="string"&&(e=new Be(e)),typeof t=="string"&&(t=new Be(t)),e.spinePos>t.spinePos)return 1;if(e.spinePosr[a].index)return 1;if(i[a].indexs.offset?1:n.offset=0&&(r.terminal.offset=t,r.steps[r.steps.length-1].type!="text"&&r.steps.push({type:"text",index:0})),r}equalStep(e,t){return!e||!t?!1:e.index===t.index&&e.id===t.id&&e.type===t.type}fromRange(e,t,i){var r={range:!1,base:{},path:{},start:null,end:null},n=e.startContainer,s=e.endContainer,a=e.startOffset,u=e.endOffset,h=!1;if(i&&(h=n.ownerDocument.querySelector("."+i)!=null),typeof t=="string"?(r.base=this.parseComponent(t),r.spinePos=r.base.steps[1].index):typeof t=="object"&&(r.base=t),e.collapsed)h&&(a=this.patchOffset(n,a,i)),r.path=this.pathTo(n,a,i);else{r.range=!0,h&&(a=this.patchOffset(n,a,i)),r.start=this.pathTo(n,a,i),h&&(u=this.patchOffset(s,u,i)),r.end=this.pathTo(s,u,i),r.path={steps:[],terminal:null};var d=r.start.steps.length,c;for(c=0;c0&&u===yt&&h===yt?r[s]=n:t===u&&(n=n+1,r[s]=n),h=u;return r}position(e){var t,i;return e.nodeType===Ut?(t=e.parentNode.children,t||(t=gr(e.parentNode)),i=Array.prototype.indexOf.call(t,e)):(t=this.textNodes(e.parentNode),i=t.indexOf(e)),i}filteredPosition(e,t){var i,r,n;return e.nodeType===Ut?(i=e.parentNode.children,n=this.normalizedMap(i,Ut,t)):(i=e.parentNode.childNodes,e.parentNode.classList.contains(t)&&(e=e.parentNode,i=e.parentNode.childNodes),n=this.normalizedMap(i,yt,t)),r=Array.prototype.indexOf.call(i,e),n[r]}stepsToXpath(e){var t=[".","*"];return e.forEach(function(i){var r=i.index+1;i.id?t.push("*[position()="+r+" and @id='"+i.id+"']"):i.type==="text"?t.push("text()["+r+"]"):t.push("*["+r+"]")}),t.join("/")}stepsToQuerySelector(e){var t=["html"];return e.forEach(function(i){var r=i.index+1;i.id?t.push("#"+i.id):i.type==="text"||t.push("*:nth-child("+r+")")}),t.join(">")}textNodes(e,t){return Array.prototype.slice.call(e.childNodes).filter(function(i){return i.nodeType===yt?!0:!!(t&&i.classList.contains(t))})}walkToNode(e,t,i){var r=t||document,n=r.documentElement,s,a,u=e.length,h;for(h=0;hh)t=t-h;else{u.nodeType===Ut?n=u.childNodes[0]:n=u;break}}return{container:n,offset:t}}toRange(e,t){var i=e||document,r,n,s,a,u,h=this,d,c,w=t?i.querySelector("."+t)!=null:!1,m;if(typeof i.createRange<"u"?r=i.createRange():r=new is,h.range?(n=h.start,d=h.path.steps.concat(n.steps),a=this.findNode(d,i,w?t:null),s=h.end,c=h.path.steps.concat(s.steps),u=this.findNode(c,i,w?t:null)):(n=h.path,d=h.path.steps,a=this.findNode(h.path.steps,i,w?t:null)),a)try{n.terminal.offset!=null?r.setStart(a,n.terminal.offset):r.setStart(a,0)}catch{m=this.fixMiss(d,n.terminal.offset,i,w?t:null),r.setStart(m.container,m.offset)}else return console.log("No startContainer found for",this.toString()),null;if(u)try{s.terminal.offset!=null?r.setEnd(u,s.terminal.offset):r.setEnd(u,0)}catch{m=this.fixMiss(c,h.end.terminal.offset,i,w?t:null),r.setEnd(m.container,m.offset)}return r}isCfiString(e){return typeof e=="string"&&e.indexOf("epubcfi(")===0&&e[e.length-1]===")"}generateChapterComponent(e,t,i){var r=parseInt(t),n=(e+1)*2,s="/"+n+"/";return s+=(r+1)*2,i&&(s+="["+i+"]"),s}collapse(e){this.range&&(this.range=!1,e?(this.path.steps=this.path.steps.concat(this.start.steps),this.path.terminal=this.start.terminal):(this.path.steps=this.path.steps.concat(this.end.steps),this.path.terminal=this.end.terminal))}}class Dt{constructor(e){this.context=e||this,this.hooks=[]}register(){for(var e=0;e-1;y&&(i=Ve(y,"head"),t=Ve(i,"base"),t||(t=y.createElement("base"),i.insertBefore(t,i.firstChild)),!n&&window&&window.location&&(r=window.location.origin+r),t.setAttribute("href",r))}function ha(y,e){var t,i,r=e.canonical;y&&(t=Ve(y,"head"),i=Ve(t,"link[rel='canonical']"),i?i.setAttribute("href",r):(i=y.createElement("link"),i.setAttribute("rel","canonical"),i.setAttribute("href",r),t.appendChild(i)))}function fa(y,e){var t,i,r=e.idref;y&&(t=Ve(y,"head"),i=Ve(t,"link[property='dc.identifier']"),i?i.setAttribute("content",r):(i=y.createElement("meta"),i.setAttribute("name","dc.identifier"),i.setAttribute("content",r),t.appendChild(i)))}function ca(y,e){var t=y.querySelectorAll("a[href]");if(t.length)for(var i=Ve(y.ownerDocument,"base"),r=i?i.getAttribute("href"):void 0,n=(function(a){var u=a.getAttribute("href");if(u.indexOf("mailto:")!==0){var h=u.indexOf("://")>-1;if(h)a.setAttribute("target","_blank");else{var d;try{d=new At(u,r)}catch{}a.onclick=function(){return d&&d.hash?e(d.Path.path+d.hash):e(d?d.Path.path:u),!1}}}}).bind(this),s=0;s=0,a;typeof XMLSerializer>"u"||s?a=Vn.DOMParser:a=XMLSerializer;var u=new a;return this.output=u.serializeToString(r),this.output}).bind(this)).then((function(){return this.hooks.serialize.trigger(this.output,this)}).bind(this)).then((function(){t.resolve(this.output)}).bind(this)).catch(function(r){t.reject(r)}),i}find(e){var t=this,i=[],r=e.toLowerCase(),n=function(s){for(var a=s.textContent.toLowerCase(),u=t.document.createRange(),h,d,c=-1,w,m=150;d!=-1;)d=a.indexOf(r,c+1),d!=-1&&(u=t.document.createRange(),u.setStart(s,d),u.setEnd(s,d+r.length),h=t.cfiFromRange(u),s.textContent.length"u")return this.find(e);let i=[];const r=150,n=this,s=e.toLowerCase(),a=function(c){const N=c.reduce((E,f)=>E+f.textContent,"").toLowerCase().indexOf(s);if(N!=-1){const f=N+s.length;let v=0,g=0;if(Nk+O.textContent.length,0);S.setEnd(b,R>f?f:f-R),D=n.cfiFromRange(S);let I=c.slice(0,v+1).reduce((k,O)=>k+O.textContent,"");I.length>r&&(I=I.substring(N-r/2,N+r/2),I="..."+I+"..."),i.push({cfi:D,excerpt:I})}}},u=document.createTreeWalker(n.document,NodeFilter.SHOW_TEXT,null,!1);let h,d=[];for(;h=u.nextNode();)d.push(h),d.length==t&&(a(d.slice(0,t)),d=d.slice(1,t));return d.length>0&&a(d),i}reconcileLayoutSettings(e){var t={layout:e.layout,spread:e.spread,orientation:e.orientation};return this.properties.forEach(function(i){var r=i.replace("rendition:",""),n=r.indexOf("-"),s,a;n!=-1&&(s=r.slice(0,n),a=r.slice(n+1),t[s]=a)}),t}cfiFromRange(e){return new Be(e,this.cfiBase).toString()}cfiFromElement(e){return new Be(e,this.cfiBase).toString()}unload(){this.document=void 0,this.contents=void 0,this.output=void 0}destroy(){this.unload(),this.hooks.serialize.clear(),this.hooks.content.clear(),this.hooks=void 0,this.idref=void 0,this.linear=void 0,this.properties=void 0,this.index=void 0,this.href=void 0,this.url=void 0,this.next=void 0,this.prev=void 0,this.cfiBase=void 0}}class pa{constructor(){this.spineItems=[],this.spineByHref={},this.spineById={},this.hooks={},this.hooks.serialize=new Dt,this.hooks.content=new Dt,this.hooks.content.register(rs),this.hooks.content.register(ha),this.hooks.content.register(fa),this.epubcfi=new Be,this.loaded=!1,this.items=void 0,this.manifest=void 0,this.spineNodeIndex=void 0,this.baseUrl=void 0,this.length=void 0}unpack(e,t,i){this.items=e.spine,this.manifest=e.manifest,this.spineNodeIndex=e.spineNodeIndex,this.baseUrl=e.baseUrl||e.basePath||"",this.length=this.items.length,this.items.forEach((r,n)=>{var s=this.manifest[r.idref],a;r.index=n,r.cfiBase=this.epubcfi.generateChapterComponent(this.spineNodeIndex,r.index,r.id),r.href&&(r.url=t(r.href,!0),r.canonical=i(r.href)),s&&(r.href=s.href,r.url=t(r.href,!0),r.canonical=i(r.href),s.properties.length&&r.properties.push.apply(r.properties,s.properties)),r.linear==="yes"?(r.prev=(function(){let u=r.index;for(;u>0;){let h=this.get(u-1);if(h&&h.linear)return h;u-=1}}).bind(this),r.next=(function(){let u=r.index;for(;u"u")for(;t-1)return delete this.spineByHref[e.href],delete this.spineById[e.idref],this.spineItems.splice(t,1)}each(){return this.spineItems.forEach.apply(this.spineItems,arguments)}first(){let e=0;do{let t=this.get(e);if(t&&t.linear)return t;e+=1}while(e=0)}destroy(){this.each(e=>e.destroy()),this.spineItems=void 0,this.spineByHref=void 0,this.spineById=void 0,this.hooks.serialize.clear(),this.hooks.content.clear(),this.hooks=void 0,this.epubcfi=void 0,this.loaded=!1,this.items=void 0,this.manifest=void 0,this.spineNodeIndex=void 0,this.baseUrl=void 0,this.length=void 0}}class Cr{constructor(e){this._q=[],this.context=e,this.tick=xr,this.running=!1,this.paused=!1}enqueue(){var e,t,i,r=[].shift.call(arguments),n=arguments;if(!r)throw new Error("No Task Provided");return typeof r=="function"?(e=new Le,t=e.promise,i={task:r,args:n,deferred:e,promise:t}):i={promise:r},this._q.push(i),this.paused==!1&&!this.running&&this.run(),i.promise}dequeue(){var e,t,i;if(this._q.length&&!this.paused){if(e=this._q.shift(),t=e.task,t)return i=t.apply(this.context,e.args),i&&typeof i.then=="function"?i.then((function(){e.deferred.resolve.apply(this.context,arguments)}).bind(this),(function(){e.deferred.reject.apply(this.context,arguments)}).bind(this)):(e.deferred.resolve.apply(this.context,i),e.promise);if(e.promise)return e.promise}else return e=new Le,e.deferred.resolve(),e.promise}dump(){for(;this._q.length;)this.dequeue()}run(){return this.running||(this.running=!0,this.defered=new Le),this.tick.call(window,()=>{this._q.length?this.dequeue().then((function(){this.run()}).bind(this)):(this.defered.resolve(),this.running=void 0)}),this.paused==!0&&(this.paused=!1),this.defered.promise}flush(){if(this.running)return this.running;if(this._q.length)return this.running=this.dequeue().then((function(){return this.running=void 0,this.flush()}).bind(this)),this.running}clear(){this._q=[]}length(){return this._q.length}pause(){this.paused=!0}stop(){this._q=[],this.running=!1,this.paused=!0}}const _i="0.3",li=["keydown","keyup","keypressed","mouseup","mousedown","mousemove","click","touchend","touchstart","touchmove"],he={BOOK:{OPEN_FAILED:"openFailed"},CONTENTS:{EXPAND:"expand",RESIZE:"resize",SELECTED:"selected",SELECTED_RANGE:"selectedRange",LINK_CLICKED:"linkClicked"},LOCATIONS:{CHANGED:"changed"},MANAGERS:{RESIZE:"resize",RESIZED:"resized",ORIENTATION_CHANGE:"orientationchange",ADDED:"added",SCROLL:"scroll",SCROLLED:"scrolled",REMOVED:"removed"},VIEWS:{AXIS:"axis",WRITING_MODE:"writingMode",LOAD_ERROR:"loaderror",RENDERED:"rendered",RESIZED:"resized",DISPLAYED:"displayed",SHOWN:"shown",HIDDEN:"hidden",MARK_CLICKED:"markClicked"},RENDITION:{STARTED:"started",ATTACHED:"attached",DISPLAYED:"displayed",DISPLAY_ERROR:"displayerror",RENDERED:"rendered",REMOVED:"removed",RESIZED:"resized",ORIENTATION_CHANGE:"orientationchange",LOCATION_CHANGED:"locationChanged",RELOCATED:"relocated",MARK_CLICKED:"markClicked",SELECTED:"selected",LAYOUT:"layout"},LAYOUT:{UPDATED:"updated"},ANNOTATION:{ATTACH:"attach",DETACH:"detach"}};class ns{constructor(e,t,i){this.spine=e,this.request=t,this.pause=i||100,this.q=new Cr(this),this.epubcfi=new Be,this._locations=[],this._locationsWords=[],this.total=0,this.break=150,this._current=0,this._wordCounter=0,this.currentLocation="",this._currentCfi="",this.processingTimeout=void 0}generate(e){return e&&(this.break=e),this.q.pause(),this.spine.each((function(t){t.linear&&this.q.enqueue(this.process.bind(this),t)}).bind(this)),this.q.run().then((function(){return this.total=this._locations.length-1,this._currentCfi&&(this.currentLocation=this._currentCfi),this._locations}).bind(this))}createRange(){return{startContainer:void 0,startOffset:void 0,endContainer:void 0,endOffset:void 0}}process(e){return e.load(this.request).then((function(t){var i=new Le,r=this.parse(t,e.cfiBase);return this._locations=this._locations.concat(r),e.unload(),this.processingTimeout=setTimeout(()=>i.resolve(r),this.pause),i.promise}).bind(this))}parse(e,t,i){var r=[],n,s=e.ownerDocument,a=Ve(s,"body"),u=0,h,d=i||this.break,c=function(w){var m=w.length,N,E=0;if(w.textContent.trim().length===0)return!1;for(u==0&&(n=this.createRange(),n.startContainer=w,n.startOffset=0),N=d-u,N>m&&(u+=m,E=m);E=m)u+=m-E,E=m;else{E+=N,n.endContainer=w,n.endOffset=E;let f=new Be(n,t).toString();r.push(f),u=0}h=w};if(pi(a,c.bind(this)),n&&n.startContainer&&h){n.endContainer=h,n.endOffset=h.length;let w=new Be(n,t).toString();r.push(w),u=0}return r}generateFromWords(e,t,i){var r=e?new Be(e):void 0;return this.q.pause(),this._locationsWords=[],this._wordCounter=0,this.spine.each((function(n){n.linear&&(r?n.index>=r.spinePos&&this.q.enqueue(this.processWords.bind(this),n,t,r,i):this.q.enqueue(this.processWords.bind(this),n,t,r,i))}).bind(this)),this.q.run().then((function(){return this._currentCfi&&(this.currentLocation=this._currentCfi),this._locationsWords}).bind(this))}processWords(e,t,i,r){return r&&this._locationsWords.length>=r?Promise.resolve():e.load(this.request).then((function(n){var s=new Le,a=this.parseWords(n,e,t,i),u=r-this._locationsWords.length;return this._locationsWords=this._locationsWords.concat(a.length>=r?a.slice(0,u):a),e.unload(),this.processingTimeout=setTimeout(()=>s.resolve(a),this.pause),s.promise}).bind(this))}countWords(e){return e=e.replace(/(^\s*)|(\s*$)/gi,""),e=e.replace(/[ ]{2,}/gi," "),e=e.replace(/\n /,` +`),e.split(" ").length}parseWords(e,t,i,r){var n=t.cfiBase,s=[],a=e.ownerDocument,u=Ve(a,"body"),h=i,d=r?r.spinePos!==t.index:!0,c;r&&t.index===r.spinePos&&(c=r.findNode(r.range?r.path.steps.concat(r.start.steps):r.path.steps,e.ownerDocument));var w=function(m){if(!d)if(m===c)d=!0;else return!1;if(m.textContent.length<10&&m.textContent.trim().length===0)return!1;var N=this.countWords(m.textContent),E,f=0;if(N===0)return!1;for(E=h-this._wordCounter,E>N&&(this._wordCounter+=N,f=N);f=N)this._wordCounter+=N-f,f=N;else{f+=E;let v=new Be(m,n);s.push({cfi:v.toString(),wordCount:this._wordCounter}),this._wordCounter=0}};return pi(u,w.bind(this)),s}locationFromCfi(e){let t;return Be.prototype.isCfiString(e)&&(e=new Be(e)),this._locations.length===0?-1:(t=Zt(e,this._locations,this.epubcfi.compare),t>this.total?this.total:t)}percentageFromCfi(e){if(this._locations.length===0)return null;var t=this.locationFromCfi(e);return this.percentageFromLocation(t)}percentageFromLocation(e){return!e||!this.total?0:e/this.total}cfiFromLocation(e){var t=-1;return typeof e!="number"&&(e=parseInt(e)),e>=0&&e1&&console.warn("Normalize cfiFromPercentage value to between 0 - 1"),e>=1){let i=new Be(this._locations[this.total]);return i.collapse(),i.toString()}return t=Math.ceil(this.total*e),this.cfiFromLocation(t)}load(e){return typeof e=="string"?this._locations=JSON.parse(e):this._locations=e,this.total=this._locations.length-1,this._locations}save(){return JSON.stringify(this._locations)}getCurrent(){return this._current}setCurrent(e){var t;if(typeof e=="string")this._currentCfi=e;else if(typeof e=="number")this._current=e;else return;this._locations.length!==0&&(typeof e=="string"?(t=this.locationFromCfi(e),this._current=t):t=e,this.emit(he.LOCATIONS.CHANGED,{percentage:this.percentageFromLocation(t)}))}get currentLocation(){return this._current}set currentLocation(e){this.setCurrent(e)}length(){return this._locations.length}destroy(){this.spine=void 0,this.request=void 0,this.pause=void 0,this.q.stop(),this.q=void 0,this.epubcfi=void 0,this._locations=void 0,this.total=void 0,this.break=void 0,this._current=void 0,this.currentLocation=void 0,this._currentCfi=void 0,clearTimeout(this.processingTimeout)}}St(ns.prototype);class va{constructor(e){this.packagePath="",this.directory="",this.encoding="",e&&this.parse(e)}parse(e){var t;if(!e)throw new Error("Container File Not Found");if(t=Ve(e,"rootfile"),!t)throw new Error("No RootFile Found");this.packagePath=t.getAttribute("full-path"),this.directory=Ot.dirname(this.packagePath),this.encoding=e.xmlEncoding}destroy(){this.packagePath=void 0,this.directory=void 0,this.encoding=void 0}}class hn{constructor(e){this.manifest={},this.navPath="",this.ncxPath="",this.coverPath="",this.spineNodeIndex=0,this.spine=[],this.metadata={},e&&this.parse(e)}parse(e){var t,i,r;if(!e)throw new Error("Package File Not Found");if(t=Ve(e,"metadata"),!t)throw new Error("No Metadata Found");if(i=Ve(e,"manifest"),!i)throw new Error("No Manifest Found");if(r=Ve(e,"spine"),!r)throw new Error("No Spine Found");return this.manifest=this.parseManifest(i),this.navPath=this.findNavPath(i),this.ncxPath=this.findNcxPath(i,r),this.coverPath=this.findCoverPath(e),this.spineNodeIndex=$n(r),this.spine=this.parseSpine(r,this.manifest),this.uniqueIdentifier=this.findUniqueIdentifier(e),this.metadata=this.parseMetadata(t),this.metadata.direction=r.getAttribute("page-progression-direction"),{metadata:this.metadata,spine:this.spine,manifest:this.manifest,navPath:this.navPath,ncxPath:this.ncxPath,coverPath:this.coverPath,spineNodeIndex:this.spineNodeIndex}}parseMetadata(e){var t={};return t.title=this.getElementText(e,"title"),t.creator=this.getElementText(e,"creator"),t.description=this.getElementText(e,"description"),t.pubdate=this.getElementText(e,"date"),t.publisher=this.getElementText(e,"publisher"),t.identifier=this.getElementText(e,"identifier"),t.language=this.getElementText(e,"language"),t.rights=this.getElementText(e,"rights"),t.modified_date=this.getPropertyText(e,"dcterms:modified"),t.layout=this.getPropertyText(e,"rendition:layout"),t.orientation=this.getPropertyText(e,"rendition:orientation"),t.flow=this.getPropertyText(e,"rendition:flow"),t.viewport=this.getPropertyText(e,"rendition:viewport"),t.media_active_class=this.getPropertyText(e,"media:active-class"),t.spread=this.getPropertyText(e,"rendition:spread"),t}parseManifest(e){var t={},i=Rt(e,"item"),r=Array.prototype.slice.call(i);return r.forEach(function(n){var s=n.getAttribute("id"),a=n.getAttribute("href")||"",u=n.getAttribute("media-type")||"",h=n.getAttribute("media-overlay")||"",d=n.getAttribute("properties")||"";t[s]={href:a,type:u,overlay:h,properties:d.length?d.split(" "):[]}}),t}parseSpine(e,t){var i=[],r=Rt(e,"itemref"),n=Array.prototype.slice.call(r);return n.forEach(function(s,a){var u=s.getAttribute("idref"),h=s.getAttribute("properties")||"",d=h.length?h.split(" "):[],c={id:s.getAttribute("id"),idref:u,linear:s.getAttribute("linear")||"yes",properties:d,index:a};i.push(c)}),i}findUniqueIdentifier(e){var t=e.documentElement.getAttribute("unique-identifier");if(!t)return"";var i=e.getElementById(t);return i&&i.localName==="identifier"&&i.namespaceURI==="http://purl.org/dc/elements/1.1/"&&i.childNodes.length>0?i.childNodes[0].nodeValue.trim():""}findNavPath(e){var t=Wt(e,"item",{properties:"nav"});return t?t.getAttribute("href"):!1}findNcxPath(e,t){var i=Wt(e,"item",{"media-type":"application/x-dtbncx+xml"}),r;return i||(r=t.getAttribute("toc"),r&&(i=e.querySelector(`#${r}`))),i?i.getAttribute("href"):!1}findCoverPath(e){var t=Ve(e,"package");t.getAttribute("version");var i=Wt(e,"item",{properties:"cover-image"});if(i)return i.getAttribute("href");var r=Wt(e,"meta",{name:"cover"});if(r){var n=r.getAttribute("content"),s=e.getElementById(n);return s?s.getAttribute("href"):""}else return!1}getElementText(e,t){var i=e.getElementsByTagNameNS("http://purl.org/dc/elements/1.1/",t),r;return!i||i.length===0?"":(r=i[0],r.childNodes.length?r.childNodes[0].nodeValue:"")}getPropertyText(e,t){var i=Wt(e,"meta",{property:t});return i&&i.childNodes.length?i.childNodes[0].nodeValue:""}load(e){this.metadata=e.metadata;let t=e.readingOrder||e.spine;return this.spine=t.map((i,r)=>(i.index=r,i.linear=i.linear||"yes",i)),e.resources.forEach((i,r)=>{this.manifest[r]=i,i.rel&&i.rel[0]==="cover"&&(this.coverPath=i.href)}),this.spineNodeIndex=0,this.toc=e.toc.map((i,r)=>(i.label=i.title,i)),{metadata:this.metadata,spine:this.spine,manifest:this.manifest,navPath:this.navPath,ncxPath:this.ncxPath,coverPath:this.coverPath,spineNodeIndex:this.spineNodeIndex,toc:this.toc}}destroy(){this.manifest=void 0,this.navPath=void 0,this.ncxPath=void 0,this.coverPath=void 0,this.spineNodeIndex=void 0,this.spine=void 0,this.metadata=void 0}}class Yi{constructor(e){this.toc=[],this.tocByHref={},this.tocById={},this.landmarks=[],this.landmarksByType={},this.length=0,e&&this.parse(e)}parse(e){let t=e.nodeType,i,r;t&&(i=Ve(e,"html"),r=Ve(e,"ncx")),t?i?(this.toc=this.parseNav(e),this.landmarks=this.parseLandmarks(e)):r&&(this.toc=this.parseNcx(e)):this.toc=this.load(e),this.length=0,this.unpack(this.toc)}unpack(e){for(var t,i=0;i(t.label=t.title,t.subitems=t.children?this.load(t.children):[],t))}forEach(e){return this.toc.forEach(e)}}var Jt={application:{ecmascript:["es","ecma"],javascript:"js",ogg:"ogx",pdf:"pdf",postscript:["ps","ai","eps","epsi","epsf","eps2","eps3"],"rdf+xml":"rdf",smil:["smi","smil"],"xhtml+xml":["xhtml","xht"],xml:["xml","xsl","xsd","opf","ncx"],zip:"zip","x-httpd-eruby":"rhtml","x-latex":"latex","x-maker":["frm","maker","frame","fm","fb","book","fbdoc"],"x-object":"o","x-shockwave-flash":["swf","swfl"],"x-silverlight":"scr","epub+zip":"epub","font-tdpfr":"pfr","inkml+xml":["ink","inkml"],json:"json","jsonml+json":"jsonml","mathml+xml":"mathml","metalink+xml":"metalink",mp4:"mp4s","omdoc+xml":"omdoc",oxps:"oxps","vnd.amazon.ebook":"azw",widget:"wgt","x-dtbook+xml":"dtb","x-dtbresource+xml":"res","x-font-bdf":"bdf","x-font-ghostscript":"gsf","x-font-linux-psf":"psf","x-font-otf":"otf","x-font-pcf":"pcf","x-font-snf":"snf","x-font-ttf":["ttf","ttc"],"x-font-type1":["pfa","pfb","pfm","afm"],"x-font-woff":"woff","x-mobipocket-ebook":["prc","mobi"],"x-mspublisher":"pub","x-nzb":"nzb","x-tgif":"obj","xaml+xml":"xaml","xml-dtd":"dtd","xproc+xml":"xpl","xslt+xml":"xslt","internet-property-stream":"acx","x-compress":"z","x-compressed":"tgz","x-gzip":"gz"},audio:{flac:"flac",midi:["mid","midi","kar","rmi"],mpeg:["mpga","mpega","mp2","mp3","m4a","mp2a","m2a","m3a"],mpegurl:"m3u",ogg:["oga","ogg","spx"],"x-aiff":["aif","aiff","aifc"],"x-ms-wma":"wma","x-wav":"wav",adpcm:"adp",mp4:"mp4a",webm:"weba","x-aac":"aac","x-caf":"caf","x-matroska":"mka","x-pn-realaudio-plugin":"rmp",xm:"xm",mid:["mid","rmi"]},image:{gif:"gif",ief:"ief",jpeg:["jpeg","jpg","jpe"],pcx:"pcx",png:"png","svg+xml":["svg","svgz"],tiff:["tiff","tif"],"x-icon":"ico",bmp:"bmp",webp:"webp","x-pict":["pic","pct"],"x-tga":"tga","cis-cod":"cod"},text:{"cache-manifest":["manifest","appcache"],css:"css",csv:"csv",html:["html","htm","shtml","stm"],mathml:"mml",plain:["txt","text","brf","conf","def","list","log","in","bas"],richtext:"rtx","tab-separated-values":"tsv","x-bibtex":"bib"},video:{mpeg:["mpeg","mpg","mpe","m1v","m2v","mp2","mpa","mpv2"],mp4:["mp4","mp4v","mpg4"],quicktime:["qt","mov"],ogg:"ogv","vnd.mpegurl":["mxu","m4u"],"x-flv":"flv","x-la-asf":["lsf","lsx"],"x-mng":"mng","x-ms-asf":["asf","asx","asr"],"x-ms-wm":"wm","x-ms-wmv":"wmv","x-ms-wmx":"wmx","x-ms-wvx":"wvx","x-msvideo":"avi","x-sgi-movie":"movie","x-matroska":["mpv","mkv","mk3d","mks"],"3gpp2":"3g2",h261:"h261",h263:"h263",h264:"h264",jpeg:"jpgv",jpm:["jpm","jpgm"],mj2:["mj2","mjp2"],"vnd.ms-playready.media.pyv":"pyv","vnd.uvvu.mp4":["uvu","uvvu"],"vnd.vivo":"viv",webm:"webm","x-f4v":"f4v","x-m4v":"m4v","x-ms-vob":"vob","x-smv":"smv"}},ga=(function(){var y,e,t,i,r={};for(y in Jt)if(Jt.hasOwnProperty(y)){for(e in Jt[y])if(Jt[y].hasOwnProperty(e))if(t=Jt[y][e],typeof t=="string")r[t]=y+"/"+e;else for(i=0;its(r)).then(r=>vr(r,i)):this.settings.request(e,"blob").then(r=>di(r,i))}replacements(){if(this.settings.replacements==="none")return new Promise((function(t){t(this.urls)}).bind(this));var e=this.urls.map(t=>{var i=this.settings.resolver(t);return this.createUrl(i).catch(r=>(console.error(r),null))});return Promise.all(e).then(t=>(this.replacementUrls=t.filter(i=>typeof i=="string"),t))}replaceCss(e,t){var i=[];return e=e||this.settings.archive,t=t||this.settings.resolver,this.cssUrls.forEach((function(r){var n=this.createCssFile(r,e,t).then((function(s){var a=this.urls.indexOf(r);a>-1&&(this.replacementUrls[a]=s)}).bind(this));i.push(n)}).bind(this)),Promise.all(i)}createCssFile(e){var t;if(Ot.isAbsolute(e))return new Promise(function(s){s()});var i=this.settings.resolver(e),r;this.settings.archive?r=this.settings.archive.getText(i):r=this.settings.request(i,"text");var n=this.urls.map(s=>{var a=this.settings.resolver(s),u=new It(i).relative(a);return u});return r?r.then(s=>(s=ln(s,n,this.replacementUrls),this.settings.replacements==="base64"?t=vr(s,"text/css"):t=di(s,"text/css"),t),s=>new Promise(function(a){a()})):new Promise(function(s){s()})}relativeTo(e,t){return t=t||this.settings.resolver,this.urls.map((function(i){var r=t(i),n=new It(e).relative(r);return n}).bind(this))}get(e){var t=this.urls.indexOf(e);if(t!==-1)return this.replacementUrls.length?new Promise((function(i,r){i(this.replacementUrls[t])}).bind(this)):this.createUrl(e)}substitute(e,t){var i;return t?i=this.relativeTo(t):i=this.urls,ln(e,i,this.replacementUrls)}destroy(){this.settings=void 0,this.manifest=void 0,this.resources=void 0,this.replacementUrls=void 0,this.html=void 0,this.assets=void 0,this.css=void 0,this.urls=void 0,this.cssUrls=void 0}}class Xi{constructor(e){this.pages=[],this.locations=[],this.epubcfi=new Be,this.firstPage=0,this.lastPage=0,this.totalPages=0,this.toc=void 0,this.ncx=void 0,e&&(this.pageList=this.parse(e)),this.pageList&&this.pageList.length&&this.process(this.pageList)}parse(e){var t=Ve(e,"html"),i=Ve(e,"ncx");if(t)return this.parseNav(e);if(i)return this.parseNcx(e)}parseNav(e){var t=vi(e,"nav","page-list"),i=t?Rt(t,"li"):[],r=i.length,n,s=[],a;if(!i||r===0)return s;for(n=0;n1?a[1]:!1,{cfi:h,href:i,packageUrl:u,page:n}):{href:i,page:n}}process(e){e.forEach(function(t){this.pages.push(t.page),t.cfi&&this.locations.push(t.cfi)},this),this.firstPage=parseInt(this.pages[0]),this.lastPage=parseInt(this.pages[this.pages.length-1]),this.totalPages=this.lastPage-this.firstPage}pageFromCfi(e){var t=-1;if(this.locations.length===0)return-1;var i=hi(e,this.locations,this.epubcfi.compare);return i!=-1?t=this.pages[i]:(i=Zt(e,this.locations,this.epubcfi.compare),t=i-1>=0?this.pages[i-1]:this.pages[0],t!==void 0||(t=-1)),t}cfiFromPage(e){var t=-1;typeof e!="number"&&(e=parseInt(e));var i=this.pages.indexOf(e);return i!=-1&&(t=this.locations[i]),t}pageFromPercentage(e){var t=Math.round(this.totalPages*e);return t}percentageFromPage(e){var t=(e-this.firstPage)/this.totalPages;return Math.round(t*1e3)/1e3}percentageFromCfi(e){var t=this.pageFromCfi(e),i=this.percentageFromPage(t);return i}destroy(){this.pages=void 0,this.locations=void 0,this.epubcfi=void 0,this.pageList=void 0,this.toc=void 0,this.ncx=void 0}}class ss{constructor(e){this.settings=e,this.name=e.layout||"reflowable",this._spread=e.spread!=="none",this._minSpreadWidth=e.minSpreadWidth||800,this._evenSpreads=e.evenSpreads||!1,e.flow==="scrolled"||e.flow==="scrolled-continuous"||e.flow==="scrolled-doc"?this._flow="scrolled":this._flow="paginated",this.width=0,this.height=0,this.spreadWidth=0,this.delta=0,this.columnWidth=0,this.gap=0,this.divisor=1,this.props={name:this.name,spread:this._spread,flow:this._flow,width:0,height:0,spreadWidth:0,delta:0,columnWidth:0,gap:0,divisor:1}}flow(e){return typeof e<"u"&&(e==="scrolled"||e==="scrolled-continuous"||e==="scrolled-doc"?this._flow="scrolled":this._flow="paginated",this.update({flow:this._flow})),this._flow}spread(e,t){return e&&(this._spread=e!=="none",this.update({spread:this._spread})),t>=0&&(this._minSpreadWidth=t),this._spread}calculate(e,t,i){var r=1,n=i||0,s=e,a=t,u=Math.floor(s/12),h,d,c,w;this._spread&&s>=this._minSpreadWidth?r=2:r=1,this.name==="reflowable"&&this._flow==="paginated"&&!(i>=0)&&(n=u%2===0?u:u-1),this.name==="pre-paginated"&&(n=0),r>1?(h=s/r-n,c=h+n):(h=s,c=s),this.name==="pre-paginated"&&r>1&&(s=h),d=h*r+n,w=s,this.width=s,this.height=a,this.spreadWidth=d,this.pageWidth=c,this.delta=w,this.columnWidth=h,this.gap=n,this.divisor=r,this.update({width:s,height:a,spreadWidth:d,pageWidth:c,delta:w,columnWidth:h,gap:n,divisor:r})}format(e,t,i){var r;return this.name==="pre-paginated"?r=e.fit(this.columnWidth,this.height,t):this._flow==="paginated"?r=e.columns(this.width,this.height,this.columnWidth,this.gap,this.settings.direction):i&&i==="horizontal"?r=e.size(null,this.height):r=e.size(this.width,null),r}count(e,t){let i,r;return this.name==="pre-paginated"?(i=1,r=1):this._flow==="paginated"?(t=t||this.delta,i=Math.ceil(e/t),r=i*this.divisor):(t=t||this.height,i=Math.ceil(e/t),r=i),{spreads:i,pages:r}}update(e){if(Object.keys(e).forEach(t=>{this.props[t]===e[t]&&delete e[t]}),Object.keys(e).length>0){let t=ut(this.props,e);this.emit(he.LAYOUT.UPDATED,t,e)}}}St(ss.prototype);class wa{constructor(e){this.rendition=e,this._themes={default:{rules:{},url:"",serialized:""}},this._overrides={},this._current="default",this._injected=[],this.rendition.hooks.content.register(this.inject.bind(this)),this.rendition.hooks.content.register(this.overrides.bind(this))}register(){if(arguments.length!==0){if(arguments.length===1&&typeof arguments[0]=="object")return this.registerThemes(arguments[0]);if(arguments.length===1&&typeof arguments[0]=="string")return this.default(arguments[0]);if(arguments.length===2&&typeof arguments[1]=="string")return this.registerUrl(arguments[0],arguments[1]);if(arguments.length===2&&typeof arguments[1]=="object")return this.registerRules(arguments[0],arguments[1])}}default(e){if(e){if(typeof e=="string")return this.registerUrl("default",e);if(typeof e=="object")return this.registerRules("default",e)}}registerThemes(e){for(var t in e)e.hasOwnProperty(t)&&(typeof e[t]=="string"?this.registerUrl(t,e[t]):this.registerRules(t,e[t]))}registerCss(e,t){this._themes[e]={serialized:t},(this._injected[e]||e=="default")&&this.update(e)}registerUrl(e,t){var i=new At(t);this._themes[e]={url:i.toString()},(this._injected[e]||e=="default")&&this.update(e)}registerRules(e,t){this._themes[e]={rules:t},(this._injected[e]||e=="default")&&this.update(e)}select(e){var t=this._current,i;this._current=e,this.update(e),i=this.rendition.getContents(),i.forEach(r=>{r.removeClass(t),r.addClass(e)})}update(e){var t=this.rendition.getContents();t.forEach(i=>{this.add(e,i)})}inject(e){var t=[],i=this._themes,r;for(var n in i)i.hasOwnProperty(n)&&(n===this._current||n==="default")&&(r=i[n],(r.rules&&Object.keys(r.rules).length>0||r.url&&t.indexOf(r.url)===-1)&&this.add(n,e),this._injected.push(n));this._current!="default"&&e.addClass(this._current)}add(e,t){var i=this._themes[e];!i||!t||(i.url?t.addStylesheet(i.url):i.serialized?(t.addStylesheetCss(i.serialized,e),i.injected=!0):i.rules&&(t.addStylesheetRules(i.rules,e),i.injected=!0))}override(e,t,i){var r=this.rendition.getContents();this._overrides[e]={value:t,priority:i===!0},r.forEach(n=>{n.css(e,this._overrides[e].value,this._overrides[e].priority)})}removeOverride(e){var t=this.rendition.getContents();delete this._overrides[e],t.forEach(i=>{i.css(e)})}overrides(e){var t=this._overrides;for(var i in t)t.hasOwnProperty(i)&&e.css(i,t[i].value,t[i].priority)}fontSize(e){this.override("font-size",e)}font(e){this.override("font-family",e,!0)}destroy(){this.rendition=void 0,this._themes=void 0,this._overrides=void 0,this._current=void 0,this._injected=void 0}}class gi{constructor(e,t,i,r=!1){this.layout=e,this.horizontal=i==="horizontal",this.direction=t||"ltr",this._dev=r}section(e){var t=this.findRanges(e),i=this.rangeListToCfiList(e.section.cfiBase,t);return i}page(e,t,i,r){var n=e&&e.document?e.document.body:!1,s;if(n){if(s=this.rangePairToCfiPair(t,{start:this.findStart(n,i,r),end:this.findEnd(n,i,r)}),this._dev===!0){let a=e.document,u=new Be(s.start).toRange(a),h=new Be(s.end).toRange(a),d=a.defaultView.getSelection(),c=a.createRange();d.removeAllRanges(),c.setStart(u.startContainer,u.startOffset),c.setEnd(h.endContainer,h.endOffset),d.addRange(c)}return s}}walk(e,t){if(!(e&&e.nodeType===Node.TEXT_NODE)){var i={acceptNode:function(u){return u.data.trim().length>0?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_REJECT}},r=i.acceptNode;r.acceptNode=i.acceptNode;for(var n=document.createTreeWalker(e,NodeFilter.SHOW_TEXT,r,!1),s,a;(s=n.nextNode())&&(a=t(s),!a););return a}}findRanges(e){for(var t=[],i=e.contents.scrollWidth(),r=Math.ceil(i/this.layout.spreadWidth),n=r*this.layout.divisor,s=this.layout.columnWidth,a=this.layout.gap,u,h,d=0;d{var h,d,c,w,m;if(m=pr(u),this.horizontal&&this.direction==="ltr"){if(h=this.horizontal?m.left:m.top,d=this.horizontal?m.right:m.bottom,h>=t&&h<=i)return u;if(d>t)return u;a=u,r.push(u)}else if(this.horizontal&&this.direction==="rtl"){if(h=m.left,d=m.right,d<=i&&d>=t)return u;if(h=t&&c<=i)return u;if(w>t)return u;a=u,r.push(u)}}),s)return this.findTextStartRange(s,t,i);return this.findTextStartRange(a,t,i)}findEnd(e,t,i){for(var r=[e],n,s=e,a;r.length;)if(n=r.shift(),a=this.walk(n,u=>{var h,d,c,w,m;if(m=pr(u),this.horizontal&&this.direction==="ltr"){if(h=Math.round(m.left),d=Math.round(m.right),h>i&&s)return s;if(d>i)return u;s=u,r.push(u)}else if(this.horizontal&&this.direction==="rtl"){if(h=Math.round(this.horizontal?m.left:m.top),d=Math.round(this.horizontal?m.right:m.bottom),di&&s)return s;if(w>i)return u;s=u,r.push(u)}}),a)return this.findTextEndRange(a,t,i);return this.findTextEndRange(s,t,i)}findTextStartRange(e,t,i){for(var r=this.splitTextNodeIntoRanges(e),n,s,a,u,h,d=0;d=t)return n}else if(this.horizontal&&this.direction==="rtl"){if(h=s.right,h<=i)return n}else if(u=s.top,u>=t)return n;return r[0]}findTextEndRange(e,t,i){for(var r=this.splitTextNodeIntoRanges(e),n,s,a,u,h,d,c,w=0;wi&&n)return n;if(h>i)return s}else if(this.horizontal&&this.direction==="rtl"){if(u=a.left,h=a.right,hi&&n)return n;if(c>i)return s}n=s}return r[r.length-1]}splitTextNodeIntoRanges(e,t){var i=[],r=e.textContent||"",n=r.trim(),s,a=e.ownerDocument,u=t||" ",h=n.indexOf(u);if(h===-1||e.nodeType!=Node.TEXT_NODE)return s=a.createRange(),s.selectNodeContents(e),[s];for(s=a.createRange(),s.setStart(e,0),s.setEnd(e,h),i.push(s),s=!1;h!=-1;)h=n.indexOf(u,h+1),h>0&&(s&&(s.setEnd(e,h),i.push(s)),s=a.createRange(),s.setStart(e,h+1));return s&&(s.setEnd(e,n.length),i.push(s)),i}rangePairToCfiPair(e,t){var i=t.start,r=t.end;i.collapse(!0),r.collapse(!1);let n=new Be(i,e).toString(),s=new Be(r,e).toString();return{start:n,end:s}}rangeListToCfiList(e,t){for(var i=[],r,n=0;n"u"?(this.resizeListeners(),this.visibilityListeners()):this.resizeObservers(),this.linksHandler()}removeListeners(){this.removeEventListeners(),this.removeSelectionListeners(),this.observer&&this.observer.disconnect(),clearTimeout(this.expanding)}resizeCheck(){let e=this.textWidth(),t=this.textHeight();(e!=this._size.width||t!=this._size.height)&&(this._size={width:e,height:t},this.onResize&&this.onResize(this._size),this.emit(he.CONTENTS.RESIZE,this._size))}resizeListeners(){clearTimeout(this.expanding),requestAnimationFrame(this.resizeCheck.bind(this)),this.expanding=setTimeout(this.resizeListeners.bind(this),350)}visibilityListeners(){document.addEventListener("visibilitychange",()=>{document.visibilityState==="visible"&&this.active===!1?(this.active=!0,this.resizeListeners()):(this.active=!1,clearTimeout(this.expanding))})}transitionListeners(){let e=this.content;e.style.transitionProperty="font, font-size, font-size-adjust, font-stretch, font-variation-settings, font-weight, width, height",e.style.transitionDuration="0.001ms",e.style.transitionTimingFunction="linear",e.style.transitionDelay="0",this._resizeCheck=this.resizeCheck.bind(this),this.document.addEventListener("transitionend",this._resizeCheck)}mediaQueryListeners(){for(var e=this.document.styleSheets,t=(function(a){a.matches&&!this._expanding&&setTimeout(this.expand.bind(this),1)}).bind(this),i=0;i{requestAnimationFrame(this.resizeCheck.bind(this))}),this.observer.observe(this.document.documentElement)}mutationObservers(){this.observer=new MutationObserver(t=>{this.resizeCheck()});let e={attributes:!0,childList:!0,characterData:!0,subtree:!0};this.observer.observe(this.document,e)}imageLoadListeners(){for(var e=this.document.querySelectorAll("img"),t,i=0;i0?(a.setStart(s,n.startOffset-2),a.setEnd(s,n.startOffset),i=a.getBoundingClientRect()):i=s.parentNode.getBoundingClientRect()}catch(u){console.error(u,u.stack)}}else i=n.getBoundingClientRect()}}else if(typeof e=="string"&&e.indexOf("#")>-1){let n=e.substring(e.indexOf("#")+1),s=this.document.getElementById(n);if(s)if(fn){let a=new Range;a.selectNode(s),i=a.getBoundingClientRect()}else i=s.getBoundingClientRect()}return i&&(r.left=i.left,r.top=i.top),r}addStylesheet(e){return new Promise((function(t,i){var r,n=!1;if(!this.document){t(!1);return}if(r=this.document.querySelector("link[href='"+e+"']"),r){t(!0);return}r=this.document.createElement("link"),r.type="text/css",r.rel="stylesheet",r.href=e,r.onload=r.onreadystatechange=function(){!n&&(!this.readyState||this.readyState=="complete")&&(n=!0,setTimeout(()=>{t(!0)},1))},this.document.head.appendChild(r)}).bind(this))}_getStylesheetNode(e){var t;return e="epubjs-inserted-css-"+(e||""),this.document?(t=this.document.getElementById(e),t||(t=this.document.createElement("style"),t.id=e,this.document.head.appendChild(t)),t):!1}addStylesheetCss(e,t){if(!this.document||!e)return!1;var i;return i=this._getStylesheetNode(t),i.innerHTML=e,!0}addStylesheetRules(e,t){var i;if(!(!this.document||!e||e.length===0))if(i=this._getStylesheetNode(t).sheet,Object.prototype.toString.call(e)==="[object Array]")for(var r=0,n=e.length;r{const N=e[m];if(Array.isArray(N))N.forEach(E=>{const v=Object.keys(E).map(g=>`${g}:${E[g]}`).join(";");i.insertRule(`${m}{${v}}`,i.cssRules.length)});else{const f=Object.keys(N).map(v=>`${v}:${N[v]}`).join(";");i.insertRule(`${m}{${f}}`,i.cssRules.length)}})}addScript(e){return new Promise((function(t,i){var r,n=!1;if(!this.document){t(!1);return}r=this.document.createElement("script"),r.type="text/javascript",r.async=!0,r.src=e,r.onload=r.onreadystatechange=function(){!n&&(!this.readyState||this.readyState=="complete")&&(n=!0,setTimeout(function(){t(!0)},1))},this.document.head.appendChild(r)}).bind(this))}addClass(e){var t;this.document&&(t=this.content||this.document.body,t&&t.classList.add(e))}removeClass(e){var t;this.document&&(t=this.content||this.document.body,t&&t.classList.remove(e))}addEventListeners(){this.document&&(this._triggerEvent=this.triggerEvent.bind(this),li.forEach(function(e){this.document.addEventListener(e,this._triggerEvent,{passive:!0})},this))}removeEventListeners(){this.document&&(li.forEach(function(e){this.document.removeEventListener(e,this._triggerEvent,{passive:!0})},this),this._triggerEvent=void 0)}triggerEvent(e){this.emit(e.type,e)}addSelectionListeners(){this.document&&(this._onSelectionChange=this.onSelectionChange.bind(this),this.document.addEventListener("selectionchange",this._onSelectionChange,{passive:!0}))}removeSelectionListeners(){this.document&&(this.document.removeEventListener("selectionchange",this._onSelectionChange,{passive:!0}),this._onSelectionChange=void 0)}onSelectionChange(e){this.selectionEndTimeout&&clearTimeout(this.selectionEndTimeout),this.selectionEndTimeout=setTimeout((function(){var t=this.window.getSelection();this.triggerSelectedEvent(t)}).bind(this),250)}triggerSelectedEvent(e){var t,i;e&&e.rangeCount>0&&(t=e.getRangeAt(0),t.collapsed||(i=new Be(t,this.cfiBase).toString(),this.emit(he.CONTENTS.SELECTED,i),this.emit(he.CONTENTS.SELECTED_RANGE,t)))}range(e,t){var i=new Be(e);return i.toRange(this.document,t)}cfiFromRange(e,t){return new Be(e,this.cfiBase,t).toString()}cfiFromNode(e,t){return new Be(e,this.cfiBase,t).toString()}map(e){var t=new gi(e);return t.section()}size(e,t){var i={scale:1,scalable:"no"};this.layoutStyle("scrolling"),e>=0&&(this.width(e),i.width=e,this.css("padding","0 "+e/12+"px")),t>=0&&(this.height(t),i.height=t),this.css("margin","0"),this.css("box-sizing","border-box"),this.viewport(i)}columns(e,t,i,r,n){let s=jt("column-axis"),a=jt("column-gap"),u=jt("column-width"),h=jt("column-fill"),c=this.writingMode().indexOf("vertical")===0?"vertical":"horizontal";this.layoutStyle("paginated"),n==="rtl"&&c==="horizontal"&&this.direction(n),this.width(e),this.height(t),this.viewport({width:e,height:t,scale:1,scalable:"no"}),this.css("overflow-y","hidden"),this.css("margin","0",!0),c==="vertical"?(this.css("padding-top",r/2+"px",!0),this.css("padding-bottom",r/2+"px",!0),this.css("padding-left","20px"),this.css("padding-right","20px"),this.css(s,"vertical")):(this.css("padding-top","20px"),this.css("padding-bottom","20px"),this.css("padding-left",r/2+"px",!0),this.css("padding-right",r/2+"px",!0),this.css(s,"horizontal")),this.css("box-sizing","border-box"),this.css("max-width","inherit"),this.css(h,"auto"),this.css(a,r+"px"),this.css(u,i+"px"),this.css("-webkit-line-box-contain","block glyphs replaced")}scaler(e,t,i){var r="scale("+e+")",n="";this.css("transform-origin","top left"),(t>=0||i>=0)&&(n=" translate("+(t||0)+"px, "+(i||0)+"px )"),this.css("transform",r+n)}fit(e,t,i){var r=this.viewport(),n=parseInt(r.width),s=parseInt(r.height),a=e/n,u=t/s,h=a{this.emit(he.CONTENTS.LINK_CLICKED,e)})}writingMode(e){let t=jt("writing-mode");return e&&this.documentElement&&(this.documentElement.style[t]=e),this.window.getComputedStyle(this.documentElement)[t]||""}layoutStyle(e){return e&&(this._layoutStyle=e,navigator.epubReadingSystem.layoutStyle=this._layoutStyle),this._layoutStyle||"paginated"}epubReadingSystem(e,t){return navigator.epubReadingSystem={name:e,version:t,layoutStyle:this.layoutStyle(),hasFeature:function(i){switch(i){case"dom-manipulation":return!0;case"layout-changes":return!0;case"touch-events":return!0;case"mouse-events":return!0;case"keyboard-events":return!0;case"spine-scripting":return!1;default:return!1}}},navigator.epubReadingSystem}destroy(){this.removeListeners()}}St(Sr.prototype);class xa{constructor(e){this.rendition=e,this.highlights=[],this.underlines=[],this.marks=[],this._annotations={},this._annotationsBySectionIndex={},this.rendition.hooks.render.register(this.inject.bind(this)),this.rendition.hooks.unloaded.register(this.clear.bind(this))}add(e,t,i,r,n,s){let a=encodeURI(t+e),h=new Be(t).spinePos,d=new os({type:e,cfiRange:t,data:i,sectionIndex:h,cb:r,className:n,styles:s});return this._annotations[a]=d,h in this._annotationsBySectionIndex?this._annotationsBySectionIndex[h].push(a):this._annotationsBySectionIndex[h]=[a],this.rendition.views().forEach(w=>{d.sectionIndex===w.index&&d.attach(w)}),d}remove(e,t){let i=encodeURI(e+t);if(i in this._annotations){let r=this._annotations[i];if(t&&r.type!==t)return;this.rendition.views().forEach(s=>{this._removeFromAnnotationBySectionIndex(r.sectionIndex,i),r.sectionIndex===s.index&&r.detach(s)}),delete this._annotations[i]}}_removeFromAnnotationBySectionIndex(e,t){this._annotationsBySectionIndex[e]=this._annotationsAt(e).filter(i=>i!==t)}_annotationsAt(e){return this._annotationsBySectionIndex[e]}highlight(e,t,i,r,n){return this.add("highlight",e,t,i,r,n)}underline(e,t,i,r,n){return this.add("underline",e,t,i,r,n)}mark(e,t,i){return this.add("mark",e,t,i)}each(){return this._annotations.forEach.apply(this._annotations,arguments)}inject(e){let t=e.index;t in this._annotationsBySectionIndex&&this._annotationsBySectionIndex[t].forEach(r=>{this._annotations[r].attach(e)})}clear(e){let t=e.index;t in this._annotationsBySectionIndex&&this._annotationsBySectionIndex[t].forEach(r=>{this._annotations[r].detach(e)})}show(){}hide(){}}class os{constructor({type:e,cfiRange:t,data:i,sectionIndex:r,cb:n,className:s,styles:a}){this.type=e,this.cfiRange=t,this.data=i,this.sectionIndex=r,this.mark=void 0,this.cb=n,this.className=s,this.styles=a}update(e){this.data=e}attach(e){let{cfiRange:t,data:i,type:r,mark:n,cb:s,className:a,styles:u}=this,h;return r==="highlight"?h=e.highlight(t,i,s,a,u):r==="underline"?h=e.underline(t,i,s,a,u):r==="mark"&&(h=e.mark(t,i,s)),this.mark=h,this.emit(he.ANNOTATION.ATTACH,h),h}detach(e){let{cfiRange:t,type:i}=this,r;return e&&(i==="highlight"?r=e.unhighlight(t):i==="underline"?r=e.ununderline(t):i==="mark"&&(r=e.unmark(t))),this.mark=void 0,this.emit(he.ANNOTATION.DETACH,r),r}text(){}}St(os.prototype);var Et={},Qt={},cn;function Aa(){if(cn)return Qt;cn=1,Object.defineProperty(Qt,"__esModule",{value:!0}),Qt.createElement=y;function y(e){return document.createElementNS("http://www.w3.org/2000/svg",e)}return Qt.default={createElement:y},Qt}var qt={},dn;function Da(){if(dn)return qt;dn=1,Object.defineProperty(qt,"__esModule",{value:!0}),qt.proxyMouse=y,qt.clone=e,qt.default={proxyMouse:y};function y(i,r){function n(h){for(var d=r.length-1;d>=0;d--){var c=r[d],w=h.clientX,m=h.clientY;if(h.touches&&h.touches.length&&(w=h.touches[0].clientX,m=h.touches[0].clientY),!!t(c,i,w,m)){c.dispatchEvent(e(h));break}}}if(i.nodeName==="iframe"||i.nodeName==="IFRAME")try{this.target=i.contentDocument}catch{this.target=i}else this.target=i;for(var s=["mouseup","mousedown","click","touchstart"],a=0;aE&&D>N}var h=i.getBoundingClientRect();if(!u(h,n,s))return!1;for(var d=i.getClientRects(),c=0,w=d.length;c1&&arguments[1]!==void 0?arguments[1]:document.body;h(this,E),this.target=f,this.element=i.default.createElement("svg"),this.marks=[],this.element.style.position="absolute",this.element.setAttribute("pointer-events","none"),n.default.proxyMouse(this.target,this.marks),this.container=v,this.container.appendChild(this.element),this.render()}return e(E,[{key:"addMark",value:function(v){var g=i.default.createElement("g");return this.element.appendChild(g),v.bind(g,this.container),this.marks.push(v),v.render(),v}},{key:"removeMark",value:function(v){var g=this.marks.indexOf(v);if(g!==-1){var D=v.unbind();this.element.removeChild(D),this.marks.splice(g,1)}}},{key:"render",value:function(){m(this.element,w(this.target,this.container));var v=!0,g=!1,D=void 0;try{for(var L=this.marks[Symbol.iterator](),b;!(v=(b=L.next()).done);v=!0){var S=b.value;S.render()}}catch(R){g=!0,D=R}finally{try{!v&&L.return&&L.return()}finally{if(g)throw D}}}}]),E})();var d=Et.Mark=(function(){function E(){h(this,E),this.element=null}return e(E,[{key:"bind",value:function(v,g){this.element=v,this.container=g}},{key:"unbind",value:function(){var v=this.element;return this.element=null,v}},{key:"render",value:function(){}},{key:"dispatchEvent",value:function(v){this.element&&this.element.dispatchEvent(v)}},{key:"getBoundingClientRect",value:function(){return this.element.getBoundingClientRect()}},{key:"getClientRects",value:function(){for(var v=[],g=this.element.firstChild;g;)v.push(g.getBoundingClientRect()),g=g.nextSibling;return v}},{key:"filteredRanges",value:function(){var v=Array.from(this.range.getClientRects());return v.filter(function(g){for(var D=0;D=E.left&&f.top>=E.top&&f.bottom<=E.bottom}return Et}var ai=Ca();class us{constructor(e,t){this.settings=ut({ignoreClass:"",axis:void 0,direction:void 0,width:0,height:0,layout:void 0,globalLayoutProperties:{},method:void 0,forceRight:!1,allowScriptedContent:!1,allowPopups:!1},t||{}),this.id="epubjs-view-"+wi(),this.section=e,this.index=e.index,this.element=this.container(this.settings.axis),this.added=!1,this.displayed=!1,this.rendered=!1,this.fixedWidth=0,this.fixedHeight=0,this.epubcfi=new Be,this.layout=this.settings.layout,this.pane=void 0,this.highlights={},this.underlines={},this.marks={}}container(e){var t=document.createElement("div");return t.classList.add("epub-view"),t.style.height="0px",t.style.width="0px",t.style.overflow="hidden",t.style.position="relative",t.style.display="block",e&&e=="horizontal"?t.style.flex="none":t.style.flex="initial",t}create(){return this.iframe?this.iframe:(this.element||(this.element=this.createContainer()),this.iframe=document.createElement("iframe"),this.iframe.id=this.id,this.iframe.scrolling="no",this.iframe.style.overflow="hidden",this.iframe.seamless="seamless",this.iframe.style.border="none",this.iframe.sandbox="allow-same-origin",this.settings.allowScriptedContent&&(this.iframe.sandbox+=" allow-scripts"),this.settings.allowPopups&&(this.iframe.sandbox+=" allow-popups"),this.iframe.setAttribute("enable-annotation","true"),this.resizing=!0,this.element.style.visibility="hidden",this.iframe.style.visibility="hidden",this.iframe.style.width="0",this.iframe.style.height="0",this._width=0,this._height=0,this.element.setAttribute("ref",this.index),this.added=!0,this.elementBounds=ui(this.element),"srcdoc"in this.iframe?this.supportsSrcdoc=!0:this.supportsSrcdoc=!1,this.settings.method||(this.settings.method=this.supportsSrcdoc?"srcdoc":"write"),this.iframe)}render(e,t){return this.create(),this.size(),this.sectionRender||(this.sectionRender=this.section.render(e)),this.sectionRender.then((function(i){return this.load(i)}).bind(this)).then((function(){let i=this.contents.writingMode(),r;return this.settings.flow==="scrolled"?r=i.indexOf("vertical")===0?"horizontal":"vertical":r=i.indexOf("vertical")===0?"vertical":"horizontal",i.indexOf("vertical")===0&&this.settings.flow==="paginated"&&(this.layout.delta=this.layout.height),this.setAxis(r),this.emit(he.VIEWS.AXIS,r),this.setWritingMode(i),this.emit(he.VIEWS.WRITING_MODE,i),this.layout.format(this.contents,this.section,this.axis),this.addListeners(),new Promise((n,s)=>{this.expand(),this.settings.forceRight&&(this.element.style.marginLeft=this.width()+"px"),n()})}).bind(this),(function(i){return this.emit(he.VIEWS.LOAD_ERROR,i),new Promise((r,n)=>{n(i)})}).bind(this)).then((function(){this.emit(he.VIEWS.RENDERED,this.section)}).bind(this))}reset(){this.iframe&&(this.iframe.style.width="0",this.iframe.style.height="0",this._width=0,this._height=0,this._textWidth=void 0,this._contentWidth=void 0,this._textHeight=void 0,this._contentHeight=void 0),this._needsReframe=!0}size(e,t){var i=e||this.settings.width,r=t||this.settings.height;this.layout.name==="pre-paginated"?this.lock("both",i,r):this.settings.axis==="horizontal"?this.lock("height",i,r):this.lock("width",i,r),this.settings.width=i,this.settings.height=r}lock(e,t,i){var r=fi(this.element),n;this.iframe?n=fi(this.iframe):n={width:0,height:0},e=="width"&&Ke(t)&&(this.lockedWidth=t-r.width-n.width),e=="height"&&Ke(i)&&(this.lockedHeight=i-r.height-n.height),e==="both"&&Ke(t)&&Ke(i)&&(this.lockedWidth=t-r.width-n.width,this.lockedHeight=i-r.height-n.height),this.displayed&&this.iframe&&this.expand()}expand(e){var t=this.lockedWidth,i=this.lockedHeight,r;!this.iframe||this._expanding||(this._expanding=!0,this.layout.name==="pre-paginated"?(t=this.layout.columnWidth,i=this.layout.height):this.settings.axis==="horizontal"?(t=this.contents.textWidth(),t%this.layout.pageWidth>0&&(t=Math.ceil(t/this.layout.pageWidth)*this.layout.pageWidth),this.settings.forceEvenPages&&(r=t/this.layout.pageWidth,this.layout.divisor>1&&this.layout.name==="reflowable"&&r%2>0&&(t+=this.layout.pageWidth))):this.settings.axis==="vertical"&&(i=this.contents.textHeight(),this.settings.flow==="paginated"&&i%this.layout.height>0&&(i=Math.ceil(i/this.layout.height)*this.layout.height)),(this._needsReframe||t!=this._width||i!=this._height)&&this.reframe(t,i),this._expanding=!1)}reframe(e,t){var i;Ke(e)&&(this.element.style.width=e+"px",this.iframe.style.width=e+"px",this._width=e),Ke(t)&&(this.element.style.height=t+"px",this.iframe.style.height=t+"px",this._height=t);let r=this.prevBounds?e-this.prevBounds.width:e,n=this.prevBounds?t-this.prevBounds.height:t;i={width:e,height:t,widthDelta:r,heightDelta:n},this.pane&&this.pane.render(),requestAnimationFrame(()=>{let s;for(let a in this.marks)this.marks.hasOwnProperty(a)&&(s=this.marks[a],this.placeMark(s.element,s.range))}),this.onResize(this,i),this.emit(he.VIEWS.RESIZED,i),this.prevBounds=i,this.elementBounds=ui(this.element)}load(e){var t=new Le,i=t.promise;if(!this.iframe)return t.reject(new Error("No Iframe Available")),i;if(this.iframe.onload=(function(n){this.onLoad(n,t)}).bind(this),this.settings.method==="blobUrl")this.blobUrl=di(e,"application/xhtml+xml"),this.iframe.src=this.blobUrl,this.element.appendChild(this.iframe);else if(this.settings.method==="srcdoc")this.iframe.srcdoc=e,this.element.appendChild(this.iframe);else{if(this.element.appendChild(this.iframe),this.document=this.iframe.contentDocument,!this.document)return t.reject(new Error("No Document Available")),i;if(this.iframe.contentDocument.open(),window.MSApp&&MSApp.execUnsafeLocalFunction){var r=this;MSApp.execUnsafeLocalFunction(function(){r.iframe.contentDocument.write(e)})}else this.iframe.contentDocument.write(e);this.iframe.contentDocument.close()}return i}onLoad(e,t){this.window=this.iframe.contentWindow,this.document=this.iframe.contentDocument,this.contents=new Sr(this.document,this.document.body,this.section.cfiBase,this.section.index),this.rendering=!1;var i=this.document.querySelector("link[rel='canonical']");i?i.setAttribute("href",this.section.canonical):(i=this.document.createElement("link"),i.setAttribute("rel","canonical"),i.setAttribute("href",this.section.canonical),this.document.querySelector("head").appendChild(i)),this.contents.on(he.CONTENTS.EXPAND,()=>{this.displayed&&this.iframe&&(this.expand(),this.contents&&this.layout.format(this.contents))}),this.contents.on(he.CONTENTS.RESIZE,r=>{this.displayed&&this.iframe&&(this.expand(),this.contents&&this.layout.format(this.contents))}),t.resolve(this.contents)}setLayout(e){this.layout=e,this.contents&&(this.layout.format(this.contents),this.expand())}setAxis(e){this.settings.axis=e,e=="horizontal"?this.element.style.flex="none":this.element.style.flex="initial",this.size()}setWritingMode(e){this.writingMode=e}addListeners(){}removeListeners(e){}display(e){var t=new Le;return this.displayed?t.resolve(this):this.render(e).then((function(){this.emit(he.VIEWS.DISPLAYED,this),this.onDisplayed(this),this.displayed=!0,t.resolve(this)}).bind(this),function(i){t.reject(i,this)}),t.promise}show(){this.element.style.visibility="visible",this.iframe&&(this.iframe.style.visibility="visible",this.iframe.style.transform="translateZ(0)",this.iframe.offsetWidth,this.iframe.style.transform=null),this.emit(he.VIEWS.SHOWN,this)}hide(){this.element.style.visibility="hidden",this.iframe.style.visibility="hidden",this.stopExpanding=!0,this.emit(he.VIEWS.HIDDEN,this)}offset(){return{top:this.element.offsetTop,left:this.element.offsetLeft}}width(){return this._width}height(){return this._height}position(){return this.element.getBoundingClientRect()}locationOf(e){this.iframe.getBoundingClientRect();var t=this.contents.locationOf(e,this.settings.ignoreClass);return{left:t.left,top:t.top}}onDisplayed(e){}onResize(e,t){}bounds(e){return(e||!this.elementBounds)&&(this.elementBounds=ui(this.element)),this.elementBounds}highlight(e,t={},i,r="epubjs-hl",n={}){if(!this.contents)return;const s=Object.assign({fill:"yellow","fill-opacity":"0.3","mix-blend-mode":"multiply"},n);let a=this.contents.range(e),u=()=>{this.emit(he.VIEWS.MARK_CLICKED,e,t)};t.epubcfi=e,this.pane||(this.pane=new ai.Pane(this.iframe,this.element));let h=new ai.Highlight(a,r,t,s),d=this.pane.addMark(h);return this.highlights[e]={mark:d,element:d.element,listeners:[u,i]},d.element.setAttribute("ref",r),d.element.addEventListener("click",u),d.element.addEventListener("touchstart",u),i&&(d.element.addEventListener("click",i),d.element.addEventListener("touchstart",i)),d}underline(e,t={},i,r="epubjs-ul",n={}){if(!this.contents)return;const s=Object.assign({stroke:"black","stroke-opacity":"0.3","mix-blend-mode":"multiply"},n);let a=this.contents.range(e),u=()=>{this.emit(he.VIEWS.MARK_CLICKED,e,t)};t.epubcfi=e,this.pane||(this.pane=new ai.Pane(this.iframe,this.element));let h=new ai.Underline(a,r,t,s),d=this.pane.addMark(h);return this.underlines[e]={mark:d,element:d.element,listeners:[u,i]},d.element.setAttribute("ref",r),d.element.addEventListener("click",u),d.element.addEventListener("touchstart",u),i&&(d.element.addEventListener("click",i),d.element.addEventListener("touchstart",i)),d}mark(e,t={},i){if(!this.contents)return;if(e in this.marks)return this.marks[e];let r=this.contents.range(e);if(!r)return;let n=r.commonAncestorContainer,s=n.nodeType===1?n:n.parentNode,a=h=>{this.emit(he.VIEWS.MARK_CLICKED,e,t)};r.collapsed&&n.nodeType===1?(r=new Range,r.selectNodeContents(n)):r.collapsed&&(r=new Range,r.selectNodeContents(s));let u=this.document.createElement("a");return u.setAttribute("ref","epubjs-mk"),u.style.position="absolute",u.dataset.epubcfi=e,t&&Object.keys(t).forEach(h=>{u.dataset[h]=t[h]}),i&&(u.addEventListener("click",i),u.addEventListener("touchstart",i)),u.addEventListener("click",a),u.addEventListener("touchstart",a),this.placeMark(u,r),this.element.appendChild(u),this.marks[e]={element:u,range:r,listeners:[a,i]},s}placeMark(e,t){let i,r,n;if(this.layout.name==="pre-paginated"||this.settings.axis!=="horizontal"){let a=t.getBoundingClientRect();i=a.top,r=a.right}else{let a=t.getClientRects(),u;for(var s=0;s!=a.length;s++)u=a[s],(!n||u.left{i&&(t.element.removeEventListener("click",i),t.element.removeEventListener("touchstart",i))}),delete this.highlights[e])}ununderline(e){let t;e in this.underlines&&(t=this.underlines[e],this.pane.removeMark(t.mark),t.listeners.forEach(i=>{i&&(t.element.removeEventListener("click",i),t.element.removeEventListener("touchstart",i))}),delete this.underlines[e])}unmark(e){let t;e in this.marks&&(t=this.marks[e],this.element.removeChild(t.element),t.listeners.forEach(i=>{i&&(t.element.removeEventListener("click",i),t.element.removeEventListener("touchstart",i))}),delete this.marks[e])}destroy(){for(let e in this.highlights)this.unhighlight(e);for(let e in this.underlines)this.ununderline(e);for(let e in this.marks)this.unmark(e);this.blobUrl&&Jn(this.blobUrl),this.displayed&&(this.displayed=!1,this.removeListeners(),this.contents.destroy(),this.stopExpanding=!0,this.element.removeChild(this.iframe),this.pane&&(this.pane.element.remove(),this.pane=void 0),this.iframe=void 0,this.contents=void 0,this._textWidth=null,this._textHeight=null,this._width=null,this._height=null)}}St(us.prototype);function Sa(){var y="reverse",e=Ta();return document.body.appendChild(e),e.scrollLeft>0?y="default":typeof Element<"u"&&Element.prototype.scrollIntoView?(e.children[0].children[1].scrollIntoView(),e.scrollLeft<0&&(y="negative")):(e.scrollLeft=1,e.scrollLeft===0&&(y="negative")),document.body.removeChild(e),y}function Ta(){var y=document.createElement("div");y.dir="rtl",y.style.position="fixed",y.style.width="1px",y.style.height="1px",y.style.top="0px",y.style.left="0px",y.style.overflow="hidden";var e=document.createElement("div");e.style.width="2px";var t=document.createElement("span");t.style.width="1px",t.style.display="inline-block";var i=document.createElement("span");return i.style.width="1px",i.style.display="inline-block",e.appendChild(t),e.appendChild(i),y.appendChild(e),y}var $i,vn;function Tr(){if(vn)return $i;vn=1;function y(e){var t=typeof e;return e!=null&&(t=="object"||t=="function")}return $i=y,$i}var Ki,gn;function ka(){if(gn)return Ki;gn=1;var y=typeof bt=="object"&&bt&&bt.Object===Object&&bt;return Ki=y,Ki}var Ji,mn;function ls(){if(mn)return Ji;mn=1;var y=ka(),e=typeof self=="object"&&self&&self.Object===Object&&self,t=y||e||Function("return this")();return Ji=t,Ji}var Qi,yn;function Na(){if(yn)return Qi;yn=1;var y=ls(),e=function(){return y.Date.now()};return Qi=e,Qi}var er,bn;function Ra(){if(bn)return er;bn=1;var y=/\s/;function e(t){for(var i=t.length;i--&&y.test(t.charAt(i)););return i}return er=e,er}var tr,wn;function Ia(){if(wn)return tr;wn=1;var y=Ra(),e=/^\s+/;function t(i){return i&&i.slice(0,y(i)+1).replace(e,"")}return tr=t,tr}var ir,_n;function hs(){if(_n)return ir;_n=1;var y=ls(),e=y.Symbol;return ir=e,ir}var rr,En;function Oa(){if(En)return rr;En=1;var y=hs(),e=Object.prototype,t=e.hasOwnProperty,i=e.toString,r=y?y.toStringTag:void 0;function n(s){var a=t.call(s,r),u=s[r];try{s[r]=void 0;var h=!0}catch{}var d=i.call(s);return h&&(a?s[r]=u:delete s[r]),d}return rr=n,rr}var nr,xn;function La(){if(xn)return nr;xn=1;var y=Object.prototype,e=y.toString;function t(i){return e.call(i)}return nr=t,nr}var sr,An;function Ba(){if(An)return sr;An=1;var y=hs(),e=Oa(),t=La(),i="[object Null]",r="[object Undefined]",n=y?y.toStringTag:void 0;function s(a){return a==null?a===void 0?r:i:n&&n in Object(a)?e(a):t(a)}return sr=s,sr}var ar,Dn;function Pa(){if(Dn)return ar;Dn=1;function y(e){return e!=null&&typeof e=="object"}return ar=y,ar}var or,Cn;function Fa(){if(Cn)return or;Cn=1;var y=Ba(),e=Pa(),t="[object Symbol]";function i(r){return typeof r=="symbol"||e(r)&&y(r)==t}return or=i,or}var ur,Sn;function za(){if(Sn)return ur;Sn=1;var y=Ia(),e=Tr(),t=Fa(),i=NaN,r=/^[-+]0x[0-9a-f]+$/i,n=/^0b[01]+$/i,s=/^0o[0-7]+$/i,a=parseInt;function u(h){if(typeof h=="number")return h;if(t(h))return i;if(e(h)){var d=typeof h.valueOf=="function"?h.valueOf():h;h=e(d)?d+"":d}if(typeof h!="string")return h===0?h:+h;h=y(h);var c=n.test(h);return c||s.test(h)?a(h.slice(2),c?2:8):r.test(h)?i:+h}return ur=u,ur}var lr,Tn;function fs(){if(Tn)return lr;Tn=1;var y=Tr(),e=Na(),t=za(),i="Expected a function",r=Math.max,n=Math.min;function s(a,u,h){var d,c,w,m,N,E,f=0,v=!1,g=!1,D=!0;if(typeof a!="function")throw new TypeError(i);u=t(u)||0,y(h)&&(v=!!h.leading,g="maxWait"in h,w=g?r(t(h.maxWait)||0,u):w,D="trailing"in h?!!h.trailing:D);function L(Z){var Y=d,X=c;return d=c=void 0,f=Z,m=a.apply(X,Y),m}function b(Z){return f=Z,N=setTimeout(I,u),v?L(Z):m}function S(Z){var Y=Z-E,X=Z-f,ie=u-Y;return g?n(ie,w-X):ie}function R(Z){var Y=Z-E,X=Z-f;return E===void 0||Y>=u||Y<0||g&&X>=w}function I(){var Z=e();if(R(Z))return k(Z);N=setTimeout(I,S(Z))}function k(Z){return N=void 0,D&&d?L(Z):(d=c=void 0,m)}function O(){N!==void 0&&clearTimeout(N),f=0,d=E=c=N=void 0}function G(){return N===void 0?m:k(e())}function j(){var Z=e(),Y=R(Z);if(d=arguments,c=this,E=Z,Y){if(N===void 0)return b(E);if(g)return clearTimeout(N),N=setTimeout(I,u),L(E)}return N===void 0&&(N=setTimeout(I,u)),m}return j.cancel=O,j.flush=G,j}return lr=s,lr}var hr,kn;function Ma(){if(kn)return hr;kn=1;var y=fs(),e=Tr(),t="Expected a function";function i(r,n,s){var a=!0,u=!0;if(typeof r!="function")throw new TypeError(t);return e(s)&&(a="leading"in s?!!s.leading:a,u="trailing"in s?!!s.trailing:u),y(r,n,{leading:a,maxWait:n,trailing:u})}return hr=i,hr}var Ua=Ma();const qa=Yt(Ua);class ja{constructor(e){this.settings=e||{},this.id="epubjs-container-"+wi(),this.container=this.create(this.settings),this.settings.hidden&&(this.wrapper=this.wrap(this.container))}create(e){let t=e.height,i=e.width,r=e.overflow||!1,n=e.axis||"vertical",s=e.direction;ut(this.settings,e),e.height&&Ke(e.height)&&(t=e.height+"px"),e.width&&Ke(e.width)&&(i=e.width+"px");let a=document.createElement("div");return a.id=this.id,a.classList.add("epub-container"),a.style.wordSpacing="0",a.style.lineHeight="0",a.style.verticalAlign="top",a.style.position="relative",n==="horizontal"&&(a.style.display="flex",a.style.flexDirection="row",a.style.flexWrap="nowrap"),i&&(a.style.width=i),t&&(a.style.height=t),r&&(r==="scroll"&&n==="vertical"?(a.style["overflow-y"]=r,a.style["overflow-x"]="hidden"):r==="scroll"&&n==="horizontal"?(a.style["overflow-y"]="hidden",a.style["overflow-x"]=r):a.style.overflow=r),s&&(a.dir=s,a.style.direction=s),s&&this.settings.fullsize&&(document.body.style.direction=s),a}wrap(e){var t=document.createElement("div");return t.style.visibility="hidden",t.style.overflow="hidden",t.style.width="0",t.style.height="0",t.appendChild(e),t}getElement(e){var t;if(Zn(e)?t=e:typeof e=="string"&&(t=document.getElementById(e)),!t)throw new Error("Not an Element");return t}attachTo(e){var t=this.getElement(e),i;if(t)return this.settings.hidden?i=this.wrapper:i=this.container,t.appendChild(i),this.element=t,t}getContainer(){return this.container}onResize(e){(!Ke(this.settings.width)||!Ke(this.settings.height))&&(this.resizeFunc=qa(e,50),window.addEventListener("resize",this.resizeFunc,!1))}onOrientationChange(e){this.orientationChangeFunc=e,window.addEventListener("orientationchange",this.orientationChangeFunc,!1)}size(e,t){var i;let r=e||this.settings.width,n=t||this.settings.height;e===null?(i=this.element.getBoundingClientRect(),i.width&&(e=Math.floor(i.width),this.container.style.width=e+"px")):Ke(e)?this.container.style.width=e+"px":this.container.style.width=e,t===null?(i=i||this.element.getBoundingClientRect(),i.height&&(t=i.height,this.container.style.height=t+"px")):Ke(t)?this.container.style.height=t+"px":this.container.style.height=t,Ke(e)||(e=this.container.clientWidth),Ke(t)||(t=this.container.clientHeight),this.containerStyles=window.getComputedStyle(this.container),this.containerPadding={left:parseFloat(this.containerStyles["padding-left"])||0,right:parseFloat(this.containerStyles["padding-right"])||0,top:parseFloat(this.containerStyles["padding-top"])||0,bottom:parseFloat(this.containerStyles["padding-bottom"])||0};let s=ci(),a=window.getComputedStyle(document.body),u={left:parseFloat(a["padding-left"])||0,right:parseFloat(a["padding-right"])||0,top:parseFloat(a["padding-top"])||0,bottom:parseFloat(a["padding-bottom"])||0};return r||(e=s.width-u.left-u.right),(this.settings.fullsize&&!n||!n)&&(t=s.height-u.top-u.bottom),{width:e-this.containerPadding.left-this.containerPadding.right,height:t-this.containerPadding.top-this.containerPadding.bottom}}bounds(){let e;return this.container.style.overflow!=="visible"&&(e=this.container&&this.container.getBoundingClientRect()),!e||!e.width||!e.height?ci():e}getSheet(){var e=document.createElement("style");return e.appendChild(document.createTextNode("")),document.head.appendChild(e),e.sheet}addStyleRules(e,t){var i="#"+this.id+" ",r="";this.sheet||(this.sheet=this.getSheet()),t.forEach(function(n){for(var s in n)n.hasOwnProperty(s)&&(r+=s+":"+n[s]+";")}),this.sheet.insertRule(i+e+" {"+r+"}",0)}axis(e){e==="horizontal"?(this.container.style.display="flex",this.container.style.flexDirection="row",this.container.style.flexWrap="nowrap"):this.container.style.display="block",this.settings.axis=e}direction(e){this.container&&(this.container.dir=e,this.container.style.direction=e),this.settings.fullsize&&(document.body.style.direction=e),this.settings.dir=e}overflow(e){this.container&&(e==="scroll"&&this.settings.axis==="vertical"?(this.container.style["overflow-y"]=e,this.container.style["overflow-x"]="hidden"):e==="scroll"&&this.settings.axis==="horizontal"?(this.container.style["overflow-y"]="hidden",this.container.style["overflow-x"]=e):this.container.style.overflow=e),this.settings.overflow=e}destroy(){this.element&&(this.settings.hidden?this.wrapper:this.container,this.element.contains(this.container)&&this.element.removeChild(this.container),window.removeEventListener("resize",this.resizeFunc),window.removeEventListener("orientationChange",this.orientationChangeFunc))}}class Wa{constructor(e){this.container=e,this._views=[],this.length=0,this.hidden=!1}all(){return this._views}first(){return this._views[0]}last(){return this._views[this._views.length-1]}indexOf(e){return this._views.indexOf(e)}slice(){return this._views.slice.apply(this._views,arguments)}get(e){return this._views[e]}append(e){return this._views.push(e),this.container&&this.container.appendChild(e.element),this.length++,e}prepend(e){return this._views.unshift(e),this.container&&this.container.insertBefore(e.element,this.container.firstChild),this.length++,e}insert(e,t){return this._views.splice(t,0,e),this.container&&(t-1&&this._views.splice(t,1),this.destroy(e),this.length--}destroy(e){e.displayed&&e.destroy(),this.container&&this.container.removeChild(e.element),e=null}forEach(){return this._views.forEach.apply(this._views,arguments)}clear(){var e,t=this.length;if(this.length){for(var i=0;i"u"&&i&&(i.toLowerCase()=="body"||i.toLowerCase()=="html")&&(this.settings.fullsize=!0),this.settings.fullsize&&(this.settings.overflow="visible",this.overflow=this.settings.overflow),this.settings.size=t,this.settings.rtlScrollType=Sa(),this.stage=new ja({width:t.width,height:t.height,overflow:this.overflow,hidden:this.settings.hidden,axis:this.settings.axis,fullsize:this.settings.fullsize,direction:this.settings.direction}),this.stage.attachTo(e),this.container=this.stage.getContainer(),this.views=new Wa(this.container),this._bounds=this.bounds(),this._stageSize=this.stage.size(),this.viewSettings.width=this._stageSize.width,this.viewSettings.height=this._stageSize.height,this.stage.onResize(this.onResized.bind(this)),this.stage.onOrientationChange(this.onOrientationChange.bind(this)),this.addEventListeners(),this.layout&&this.updateLayout(),this.rendered=!0}addEventListeners(){var e;window.addEventListener("unload",(function(t){this.destroy()}).bind(this)),this.settings.fullsize?e=window:e=this.container,this._onScroll=this.onScroll.bind(this),e.addEventListener("scroll",this._onScroll)}removeEventListeners(){var e;this.settings.fullsize?e=window:e=this.container,e.removeEventListener("scroll",this._onScroll),this._onScroll=void 0}destroy(){clearTimeout(this.orientationTimeout),clearTimeout(this.resizeTimeout),clearTimeout(this.afterScrolled),this.clear(),this.removeEventListeners(),this.stage.destroy(),this.rendered=!1}onOrientationChange(e){let{orientation:t}=window;this.optsSettings.resizeOnOrientationChange&&this.resize(),clearTimeout(this.orientationTimeout),this.orientationTimeout=setTimeout((function(){this.orientationTimeout=void 0,this.optsSettings.resizeOnOrientationChange&&this.resize(),this.emit(he.MANAGERS.ORIENTATION_CHANGE,t)}).bind(this),500)}onResized(e){this.resize()}resize(e,t,i){let r=this.stage.size(e,t);if(this.winBounds=ci(),this.orientationTimeout&&this.winBounds.width===this.winBounds.height){this._stageSize=void 0;return}this._stageSize&&this._stageSize.width===r.width&&this._stageSize.height===r.height||(this._stageSize=r,this._bounds=this.bounds(),this.clear(),this.viewSettings.width=this._stageSize.width,this.viewSettings.height=this._stageSize.height,this.updateLayout(),this.emit(he.MANAGERS.RESIZED,{width:this._stageSize.width,height:this._stageSize.height},i))}createView(e,t){return new this.View(e,ut(this.viewSettings,{forceRight:t}))}handleNextPrePaginated(e,t,i){let r;if(this.layout.name==="pre-paginated"&&this.layout.divisor>1){if(e||t.index===0)return;if(r=t.next(),r&&!r.properties.includes("page-spread-left"))return i.call(this,r)}}display(e,t){var i=new Le,r=i.promise;(t===e.href||Ke(t))&&(t=void 0);var n=this.views.find(e);if(n&&e&&this.layout.name!=="pre-paginated"){let a=n.offset();if(this.settings.direction==="ltr")this.scrollTo(a.left,a.top,!0);else{let u=n.width();this.scrollTo(a.left+u,a.top,!0)}if(t){let u=n.locationOf(t),h=n.width();this.moveTo(u,h)}return i.resolve(),r}this.clear();let s=!1;return this.layout.name==="pre-paginated"&&this.layout.divisor===2&&e.properties.includes("page-spread-right")&&(s=!0),this.add(e,s).then((function(a){if(t){let u=a.locationOf(t),h=a.width();this.moveTo(u,h)}}).bind(this),a=>{i.reject(a)}).then((function(){return this.handleNextPrePaginated(s,e,this.add)}).bind(this)).then((function(){this.views.show(),i.resolve()}).bind(this)),r}afterDisplayed(e){this.emit(he.MANAGERS.ADDED,e)}afterResized(e){this.emit(he.MANAGERS.RESIZE,e.section)}moveTo(e,t){var i=0,r=0;this.isPaginated?(i=Math.floor(e.left/this.layout.delta)*this.layout.delta,i+this.layout.delta>this.container.scrollWidth&&(i=this.container.scrollWidth-this.layout.delta),r=Math.floor(e.top/this.layout.delta)*this.layout.delta,r+this.layout.delta>this.container.scrollHeight&&(r=this.container.scrollHeight-this.layout.delta)):r=e.top,this.settings.direction==="rtl"&&(i=i+this.layout.delta,i=i-t),this.scrollTo(i,r,!0)}add(e,t){var i=this.createView(e,t);return this.views.append(i),i.onDisplayed=this.afterDisplayed.bind(this),i.onResize=this.afterResized.bind(this),i.on(he.VIEWS.AXIS,r=>{this.updateAxis(r)}),i.on(he.VIEWS.WRITING_MODE,r=>{this.updateWritingMode(r)}),i.display(this.request)}append(e,t){var i=this.createView(e,t);return this.views.append(i),i.onDisplayed=this.afterDisplayed.bind(this),i.onResize=this.afterResized.bind(this),i.on(he.VIEWS.AXIS,r=>{this.updateAxis(r)}),i.on(he.VIEWS.WRITING_MODE,r=>{this.updateWritingMode(r)}),i.display(this.request)}prepend(e,t){var i=this.createView(e,t);return i.on(he.VIEWS.RESIZED,r=>{this.counter(r)}),this.views.prepend(i),i.onDisplayed=this.afterDisplayed.bind(this),i.onResize=this.afterResized.bind(this),i.on(he.VIEWS.AXIS,r=>{this.updateAxis(r)}),i.on(he.VIEWS.WRITING_MODE,r=>{this.updateWritingMode(r)}),i.display(this.request)}counter(e){this.settings.axis==="vertical"?this.scrollBy(0,e.heightDelta,!0):this.scrollBy(e.widthDelta,0,!0)}next(){var e,t;let i=this.settings.direction;if(this.views.length&&(this.isPaginated&&this.settings.axis==="horizontal"&&(!i||i==="ltr")?(this.scrollLeft=this.container.scrollLeft,t=this.container.scrollLeft+this.container.offsetWidth+this.layout.delta,t<=this.container.scrollWidth?this.scrollBy(this.layout.delta,0,!0):e=this.views.last().section.next()):this.isPaginated&&this.settings.axis==="horizontal"&&i==="rtl"?(this.scrollLeft=this.container.scrollLeft,this.settings.rtlScrollType==="default"?(t=this.container.scrollLeft,t>0?this.scrollBy(this.layout.delta,0,!0):e=this.views.last().section.next()):(t=this.container.scrollLeft+this.layout.delta*-1,t>this.container.scrollWidth*-1?this.scrollBy(this.layout.delta,0,!0):e=this.views.last().section.next())):this.isPaginated&&this.settings.axis==="vertical"?(this.scrollTop=this.container.scrollTop,this.container.scrollTop+this.container.offsetHeightn).then((function(){!this.isPaginated&&this.settings.axis==="horizontal"&&this.settings.direction==="rtl"&&this.settings.rtlScrollType==="default"&&this.scrollTo(this.container.scrollWidth,0,!0),this.views.show()}).bind(this))}}prev(){var e,t;let i=this.settings.direction;if(this.views.length&&(this.isPaginated&&this.settings.axis==="horizontal"&&(!i||i==="ltr")?(this.scrollLeft=this.container.scrollLeft,t=this.container.scrollLeft,t>0?this.scrollBy(-this.layout.delta,0,!0):e=this.views.first().section.prev()):this.isPaginated&&this.settings.axis==="horizontal"&&i==="rtl"?(this.scrollLeft=this.container.scrollLeft,this.settings.rtlScrollType==="default"?(t=this.container.scrollLeft+this.container.offsetWidth,t0?this.scrollBy(0,-this.layout.height,!0):e=this.views.first().section.prev()):e=this.views.first().section.prev(),e)){this.clear(),this.updateLayout();let r=!1;return this.layout.name==="pre-paginated"&&this.layout.divisor===2&&typeof e.prev()!="object"&&(r=!0),this.prepend(e,r).then((function(){var n;if(this.layout.name==="pre-paginated"&&this.layout.divisor>1&&(n=e.prev(),n))return this.prepend(n)}).bind(this),n=>n).then((function(){this.isPaginated&&this.settings.axis==="horizontal"&&(this.settings.direction==="rtl"?this.settings.rtlScrollType==="default"?this.scrollTo(0,0,!0):this.scrollTo(this.container.scrollWidth*-1+this.layout.delta,0,!0):this.scrollTo(this.container.scrollWidth-this.layout.delta,0,!0)),this.views.show()}).bind(this))}}current(){var e=this.visible();return e.length?e[e.length-1]:null}clear(){this.views&&(this.views.hide(),this.scrollTo(0,0,!0),this.views.clear())}currentLocation(){return this.updateLayout(),this.isPaginated&&this.settings.axis==="horizontal"?this.location=this.paginatedLocation():this.location=this.scrolledLocation(),this.location}scrolledLocation(){let e=this.visible(),t=this.container.getBoundingClientRect(),i=t.height{let{index:d,href:c}=h.section,w=h.position(),m=h.width(),N=h.height(),E,f,v,g;n?(E=s+t.top-w.top+a,f=E+i-a,g=this.layout.count(N,i).pages,v=i):(E=s+t.left-w.left+a,f=E+r-a,g=this.layout.count(m,r).pages,v=r);let D=Math.ceil(E/v),L=[],b=Math.ceil(f/v);if(this.settings.direction==="rtl"&&!n){let I=D;D=g-b,b=g-I}L=[];for(var S=D;S<=b;S++){let I=S+1;L.push(I)}let R=this.mapping.page(h.contents,h.section.cfiBase,E,f);return{index:d,href:c,pages:L,totalPages:g,mapping:R}})}paginatedLocation(){let e=this.visible(),t=this.container.getBoundingClientRect(),i=0,r=0;return this.settings.fullsize&&(i=window.scrollX),e.map(s=>{let{index:a,href:u}=s.section,h,d=s.position(),c=s.width(),w,m,N;this.settings.direction==="rtl"?(h=t.right-i,N=Math.min(Math.abs(h-d.left),this.layout.width)-r,m=d.width-(d.right-h)-r,w=m-N):(h=t.left+i,N=Math.min(d.right-h,this.layout.width)-r,w=h-d.left+r,m=w+N),r+=N;let E=this.mapping.page(s.contents,s.section.cfiBase,w,m),f=this.layout.count(c).pages,v=Math.floor(w/this.layout.pageWidth),g=[],D=Math.floor(m/this.layout.pageWidth);if(v<0&&(v=0,D=D+1),this.settings.direction==="rtl"){let b=v;v=f-D,D=f-b}for(var L=v+1;L<=D;L++){let b=L;g.push(b)}return{index:a,href:u,pages:g,totalPages:f,mapping:E}})}isVisible(e,t,i,r){var n=e.position(),s=r||this.bounds();return this.settings.axis==="horizontal"&&n.right>s.left-t&&n.lefts.top-t&&n.top0&&this.layout.name==="pre-paginated"&&this.display(this.views.first().section)}updateLayout(){this.stage&&(this._stageSize=this.stage.size(),this.isPaginated?(this.layout.calculate(this._stageSize.width,this._stageSize.height,this.settings.gap),this.settings.offset=this.layout.delta/this.layout.divisor):this.layout.calculate(this._stageSize.width,this._stageSize.height),this.viewSettings.width=this.layout.width,this.viewSettings.height=this.layout.height,this.setLayout(this.layout))}setLayout(e){this.viewSettings.layout=e,this.mapping=new gi(e.props,this.settings.direction,this.settings.axis),this.views&&this.views.forEach(function(t){t&&t.setLayout(e)})}updateWritingMode(e){this.writingMode=e}updateAxis(e,t){!t&&e===this.settings.axis||(this.settings.axis=e,this.stage&&this.stage.axis(e),this.viewSettings.axis=e,this.mapping&&(this.mapping=new gi(this.layout.props,this.settings.direction,this.settings.axis)),this.layout&&(e==="vertical"?this.layout.spread("none"):this.layout.spread(this.layout.settings.spread)))}updateFlow(e,t="auto"){let i=e==="paginated"||e==="auto";this.isPaginated=i,e==="scrolled-doc"||e==="scrolled-continuous"||e==="scrolled"?this.updateAxis("vertical"):this.updateAxis("horizontal"),this.viewSettings.flow=e,this.settings.overflow?this.overflow=this.settings.overflow:this.overflow=i?"hidden":t,this.stage&&this.stage.overflow(this.overflow),this.updateLayout()}getContents(){var e=[];return this.views&&this.views.forEach(function(t){const i=t&&t.contents;i&&e.push(i)}),e}direction(e="ltr"){this.settings.direction=e,this.stage&&this.stage.direction(e),this.viewSettings.direction=e,this.updateLayout()}isRendered(){return this.rendered}}St(mi.prototype);const Ha={easeInCubic:function(y){return Math.pow(y,3)}};class yr{constructor(e,t){this.settings=ut({duration:80,minVelocity:.2,minDistance:10,easing:Ha.easeInCubic},t||{}),this.supportsTouch=this.supportsTouch(),this.supportsTouch&&this.setup(e)}setup(e){this.manager=e,this.layout=this.manager.layout,this.fullsize=this.manager.settings.fullsize,this.fullsize?(this.element=this.manager.stage.element,this.scroller=window,this.disableScroll()):(this.element=this.manager.stage.container,this.scroller=this.element,this.element.style.WebkitOverflowScrolling="touch"),this.manager.settings.offset=this.layout.width,this.manager.settings.afterScrolledTimeout=this.settings.duration*2,this.isVertical=this.manager.settings.axis==="vertical",!(!this.manager.isPaginated||this.isVertical)&&(this.touchCanceler=!1,this.resizeCanceler=!1,this.snapping=!1,this.scrollLeft,this.scrollTop,this.startTouchX=void 0,this.startTouchY=void 0,this.startTime=void 0,this.endTouchX=void 0,this.endTouchY=void 0,this.endTime=void 0,this.addListeners())}supportsTouch(){return!!("ontouchstart"in window||window.DocumentTouch&&document instanceof DocumentTouch)}disableScroll(){this.element.style.overflow="hidden"}enableScroll(){this.element.style.overflow=""}addListeners(){this._onResize=this.onResize.bind(this),window.addEventListener("resize",this._onResize),this._onScroll=this.onScroll.bind(this),this.scroller.addEventListener("scroll",this._onScroll),this._onTouchStart=this.onTouchStart.bind(this),this.scroller.addEventListener("touchstart",this._onTouchStart,{passive:!0}),this.on("touchstart",this._onTouchStart),this._onTouchMove=this.onTouchMove.bind(this),this.scroller.addEventListener("touchmove",this._onTouchMove,{passive:!0}),this.on("touchmove",this._onTouchMove),this._onTouchEnd=this.onTouchEnd.bind(this),this.scroller.addEventListener("touchend",this._onTouchEnd,{passive:!0}),this.on("touchend",this._onTouchEnd),this._afterDisplayed=this.afterDisplayed.bind(this),this.manager.on(he.MANAGERS.ADDED,this._afterDisplayed)}removeListeners(){window.removeEventListener("resize",this._onResize),this._onResize=void 0,this.scroller.removeEventListener("scroll",this._onScroll),this._onScroll=void 0,this.scroller.removeEventListener("touchstart",this._onTouchStart,{passive:!0}),this.off("touchstart",this._onTouchStart),this._onTouchStart=void 0,this.scroller.removeEventListener("touchmove",this._onTouchMove,{passive:!0}),this.off("touchmove",this._onTouchMove),this._onTouchMove=void 0,this.scroller.removeEventListener("touchend",this._onTouchEnd,{passive:!0}),this.off("touchend",this._onTouchEnd),this._onTouchEnd=void 0,this.manager.off(he.MANAGERS.ADDED,this._afterDisplayed),this._afterDisplayed=void 0}afterDisplayed(e){let t=e.contents;["touchstart","touchmove","touchend"].forEach(i=>{t.on(i,r=>this.triggerViewEvent(r,t))})}triggerViewEvent(e,t){this.emit(e.type,e,t)}onScroll(e){this.scrollLeft=this.fullsize?window.scrollX:this.scroller.scrollLeft,this.scrollTop=this.fullsize?window.scrollY:this.scroller.scrollTop}onResize(e){this.resizeCanceler=!0}onTouchStart(e){let{screenX:t,screenY:i}=e.touches[0];this.fullsize&&this.enableScroll(),this.touchCanceler=!0,this.startTouchX||(this.startTouchX=t,this.startTouchY=i,this.startTime=this.now()),this.endTouchX=t,this.endTouchY=i,this.endTime=this.now()}onTouchMove(e){let{screenX:t,screenY:i}=e.touches[0],r=Math.abs(i-this.endTouchY);this.touchCanceler=!0,!this.fullsize&&r<10&&(this.element.scrollLeft-=t-this.endTouchX),this.endTouchX=t,this.endTouchY=i,this.endTime=this.now()}onTouchEnd(e){this.fullsize&&this.disableScroll(),this.touchCanceler=!1;let t=this.wasSwiped();t!==0?this.snap(t):this.snap(),this.startTouchX=void 0,this.startTouchY=void 0,this.startTime=void 0,this.endTouchX=void 0,this.endTouchY=void 0,this.endTime=void 0}wasSwiped(){let e=this.layout.pageWidth*this.layout.divisor,t=this.endTouchX-this.startTouchX,i=Math.abs(t),r=this.endTime-this.startTime,n=t/r,s=this.settings.minVelocity;if(i<=this.settings.minDistance||i>=e)return 0;if(n>s)return-1;if(n<-s)return 1}needsSnap(){let e=this.scrollLeft,t=this.layout.pageWidth*this.layout.divisor;return e%t!==0}snap(e=0){let t=this.scrollLeft,i=this.layout.pageWidth*this.layout.divisor,r=Math.round(t/i)*i;return e&&(r+=e*i),this.smoothScrollTo(r)}smoothScrollTo(e){const t=new Le,i=this.scrollLeft,r=this.now(),n=this.settings.duration,s=this.settings.easing;this.snapping=!0;function a(){const u=this.now(),h=Math.min(1,(u-r)/n);if(s(h),this.touchCanceler||this.resizeCanceler){this.resizeCanceler=!1,this.snapping=!1,t.resolve();return}h<1?(window.requestAnimationFrame(a.bind(this)),this.scrollTo(i+(e-i)*h,0)):(this.scrollTo(e,0),this.snapping=!1,t.resolve())}return a.call(this),t.promise}scrollTo(e=0,t=0){this.fullsize?window.scroll(e,t):(this.scroller.scrollLeft=e,this.scroller.scrollTop=t)}now(){return"now"in window.performance?performance.now():new Date().getTime()}destroy(){this.scroller&&(this.fullsize&&this.enableScroll(),this.removeListeners(),this.scroller=void 0)}}St(yr.prototype);var Va=fs();const Ga=Yt(Va);class Za extends mi{constructor(e){super(e),this.name="continuous",this.settings=ut(this.settings||{},{infinite:!0,overflow:void 0,axis:void 0,writingMode:void 0,flow:"scrolled",offset:500,offsetDelta:250,width:void 0,height:void 0,snap:!1,afterScrolledTimeout:10,allowScriptedContent:!1,allowPopups:!1}),ut(this.settings,e.settings||{}),e.settings.gap!="undefined"&&e.settings.gap===0&&(this.settings.gap=e.settings.gap),this.viewSettings={ignoreClass:this.settings.ignoreClass,axis:this.settings.axis,flow:this.settings.flow,layout:this.layout,width:0,height:0,forceEvenPages:!1,allowScriptedContent:this.settings.allowScriptedContent,allowPopups:this.settings.allowPopups},this.scrollTop=0,this.scrollLeft=0}display(e,t){return mi.prototype.display.call(this,e,t).then((function(){return this.fill()}).bind(this))}fill(e){var t=e||new Le;return this.q.enqueue(()=>this.check()).then(i=>{i?this.fill(t):t.resolve()}),t.promise}moveTo(e){var t=0,i=0;this.isPaginated?(t=Math.floor(e.left/this.layout.delta)*this.layout.delta,t+this.settings.offsetDelta):(i=e.top,e.top+this.settings.offsetDelta),(t>0||i>0)&&this.scrollBy(t,i,!0)}afterResized(e){this.emit(he.MANAGERS.RESIZE,e.section)}removeShownListeners(e){e.onDisplayed=function(){}}add(e){var t=this.createView(e);return this.views.append(t),t.on(he.VIEWS.RESIZED,i=>{t.expanded=!0}),t.on(he.VIEWS.AXIS,i=>{this.updateAxis(i)}),t.on(he.VIEWS.WRITING_MODE,i=>{this.updateWritingMode(i)}),t.onDisplayed=this.afterDisplayed.bind(this),t.onResize=this.afterResized.bind(this),t.display(this.request)}append(e){var t=this.createView(e);return t.on(he.VIEWS.RESIZED,i=>{t.expanded=!0}),t.on(he.VIEWS.AXIS,i=>{this.updateAxis(i)}),t.on(he.VIEWS.WRITING_MODE,i=>{this.updateWritingMode(i)}),this.views.append(t),t.onDisplayed=this.afterDisplayed.bind(this),t}prepend(e){var t=this.createView(e);return t.on(he.VIEWS.RESIZED,i=>{this.counter(i),t.expanded=!0}),t.on(he.VIEWS.AXIS,i=>{this.updateAxis(i)}),t.on(he.VIEWS.WRITING_MODE,i=>{this.updateWritingMode(i)}),this.views.prepend(t),t.onDisplayed=this.afterDisplayed.bind(this),t}counter(e){this.settings.axis==="vertical"?this.scrollBy(0,e.heightDelta,!0):this.scrollBy(e.widthDelta,0,!0)}update(e){for(var t=this.bounds(),i=this.views.all(),r=i.length,n=typeof e<"u"?e:this.settings.offset||0,s,a,u=new Le,h=[],d=0;d{a.hide()});h.push(c)}else this.q.enqueue(a.destroy.bind(a)),clearTimeout(this.trimTimeout),this.trimTimeout=setTimeout((function(){this.q.enqueue(this.trim.bind(this))}).bind(this),250);return h.length?Promise.all(h).catch(c=>{u.reject(c)}):(u.resolve(),u.promise)}check(e,t){var i=new Le,r=[],n=this.settings.axis==="horizontal",s=this.settings.offset||0;e&&n&&(s=e),t&&!n&&(s=t);var a=this._bounds;let u=n?this.scrollLeft:this.scrollTop,h=n?Math.floor(a.width):a.height,d=n?this.container.scrollWidth:this.container.scrollHeight,c=this.writingMode&&this.writingMode.indexOf("vertical")===0?"vertical":"horizontal",w=this.settings.rtlScrollType,m=this.settings.direction==="rtl";this.settings.fullsize?(n&&m&&w==="negative"||!n&&m&&w==="default")&&(u=u*-1):(m&&w==="default"&&c==="horizontal"&&(u=d-h-u),m&&w==="negative"&&c==="horizontal"&&(u=u*-1));let N=()=>{let D=this.views.first(),L=D&&D.section.prev();L&&r.push(this.prepend(L))},E=()=>{let D=this.views.last(),L=D&&D.section.next();L&&r.push(this.append(L))},f=u+h+s,v=u-s;f>=d&&E(),v<0&&N();let g=r.map(D=>D.display(this.request));return r.length?Promise.all(g).then(()=>this.check()).then(()=>this.update(s),D=>D):(this.q.enqueue((function(){this.update()}).bind(this)),i.resolve(!1),i.promise)}trim(){for(var e=new Le,t=this.views.displayed(),i=t[0],r=t[t.length-1],n=this.views.indexOf(i),s=this.views.indexOf(r),a=this.views.slice(0,n),u=this.views.slice(s+1),h=0;h{t.resolve(r),this.displaying=void 0,this.emit(he.RENDITION.DISPLAYED,r),this.reportLocation()},n=>{this.emit(he.RENDITION.DISPLAY_ERROR,n)}),i):(t.reject(new Error("No Section Found")),i)}}afterDisplayed(e){e.on(he.VIEWS.MARK_CLICKED,(t,i)=>this.triggerMarkEvent(t,i,e.contents)),this.hooks.render.trigger(e,this).then(()=>{e.contents?this.hooks.content.trigger(e.contents,this).then(()=>{this.emit(he.RENDITION.RENDERED,e.section,e)}):this.emit(he.RENDITION.RENDERED,e.section,e)})}afterRemoved(e){this.hooks.unloaded.trigger(e,this).then(()=>{this.emit(he.RENDITION.REMOVED,e.section,e)})}onResized(e,t){this.emit(he.RENDITION.RESIZED,{width:e.width,height:e.height},t),this.location&&this.location.start&&this.display(t||this.location.start.cfi)}onOrientationChange(e){this.emit(he.RENDITION.ORIENTATION_CHANGE,e)}moveTo(e){this.manager.moveTo(e)}resize(e,t,i){e&&(this.settings.width=e),t&&(this.settings.height=t),this.manager.resize(e,t,i)}clear(){this.manager.clear()}next(){return this.q.enqueue(this.manager.next.bind(this.manager)).then(this.reportLocation.bind(this))}prev(){return this.q.enqueue(this.manager.prev.bind(this.manager)).then(this.reportLocation.bind(this))}determineLayoutProperties(e){var t,i=this.settings.layout||e.layout||"reflowable",r=this.settings.spread||e.spread||"auto",n=this.settings.orientation||e.orientation||"auto",s=this.settings.flow||e.flow||"auto",a=e.viewport||"",u=this.settings.minSpreadWidth||e.minSpreadWidth||800,h=this.settings.direction||e.direction||"ltr";return(this.settings.width===0||this.settings.width>0)&&(this.settings.height===0||this.settings.height>0),t={layout:i,spread:r,orientation:n,flow:s,viewport:a,minSpreadWidth:u,direction:h},t}flow(e){var t=e;(e==="scrolled"||e==="scrolled-doc"||e==="scrolled-continuous")&&(t="scrolled"),(e==="auto"||e==="paginated")&&(t="paginated"),this.settings.flow=e,this._layout&&this._layout.flow(t),this.manager&&this._layout&&this.manager.applyLayout(this._layout),this.manager&&this.manager.updateFlow(t),this.manager&&this.manager.isRendered()&&this.location&&(this.manager.clear(),this.display(this.location.start.cfi))}layout(e){return e&&(this._layout=new ss(e),this._layout.spread(e.spread,this.settings.minSpreadWidth),this._layout.on(he.LAYOUT.UPDATED,(t,i)=>{this.emit(he.RENDITION.LAYOUT,t,i)})),this.manager&&this._layout&&this.manager.applyLayout(this._layout),this._layout}spread(e,t){this.settings.spread=e,t&&(this.settings.minSpreadWidth=t),this._layout&&this._layout.spread(e,t),this.manager&&this.manager.isRendered()&&this.manager.updateLayout()}direction(e){this.settings.direction=e||"ltr",this.manager&&this.manager.direction(this.settings.direction),this.manager&&this.manager.isRendered()&&this.location&&(this.manager.clear(),this.display(this.location.start.cfi))}reportLocation(){return this.q.enqueue((function(){requestAnimationFrame((function(){var i=this.manager.currentLocation();if(i&&i.then&&typeof i.then=="function")i.then((function(r){let n=this.located(r);!n||!n.start||!n.end||(this.location=n,this.emit(he.RENDITION.LOCATION_CHANGED,{index:this.location.start.index,href:this.location.start.href,start:this.location.start.cfi,end:this.location.end.cfi,percentage:this.location.start.percentage}),this.emit(he.RENDITION.RELOCATED,this.location))}).bind(this));else if(i){let r=this.located(i);if(!r||!r.start||!r.end)return;this.location=r,this.emit(he.RENDITION.LOCATION_CHANGED,{index:this.location.start.index,href:this.location.start.href,start:this.location.start.cfi,end:this.location.end.cfi,percentage:this.location.start.percentage}),this.emit(he.RENDITION.RELOCATED,this.location)}}).bind(this))}).bind(this))}currentLocation(){var e=this.manager.currentLocation();if(e&&e.then&&typeof e.then=="function")e.then((function(t){return this.located(t)}).bind(this));else if(e)return this.located(e)}located(e){if(!e.length)return{};let t=e[0],i=e[e.length-1],r={start:{index:t.index,href:t.href,cfi:t.mapping.start,displayed:{page:t.pages[0]||1,total:t.totalPages}},end:{index:i.index,href:i.href,cfi:i.mapping.end,displayed:{page:i.pages[i.pages.length-1]||1,total:i.totalPages}}},n=this.book.locations.locationFromCfi(t.mapping.start),s=this.book.locations.locationFromCfi(i.mapping.end);n!=null&&(r.start.location=n,r.start.percentage=this.book.locations.percentageFromLocation(n)),s!=null&&(r.end.location=s,r.end.percentage=this.book.locations.percentageFromLocation(s));let a=this.book.pageList.pageFromCfi(t.mapping.start),u=this.book.pageList.pageFromCfi(i.mapping.end);return a!=-1&&(r.start.page=a),u!=-1&&(r.end.page=u),i.index===this.book.spine.last().index&&r.end.displayed.page>=r.end.displayed.total&&(r.atEnd=!0),t.index===this.book.spine.first().index&&r.start.displayed.page===1&&(r.atStart=!0),r}destroy(){this.manager&&this.manager.destroy(),this.book=void 0}passEvents(e){li.forEach(t=>{e.on(t,i=>this.triggerViewEvent(i,e))}),e.on(he.CONTENTS.SELECTED,t=>this.triggerSelectedEvent(t,e))}triggerViewEvent(e,t){this.emit(e.type,e,t)}triggerSelectedEvent(e,t){this.emit(he.RENDITION.SELECTED,e,t)}triggerMarkEvent(e,t,i){this.emit(he.RENDITION.MARK_CLICKED,e,t,i)}getRange(e,t){var i=new Be(e),r=this.manager.visible().filter(function(n){if(i.spinePos===n.index)return!0});if(r.length)return r[0].contents.range(i,t)}adjustImages(e){if(this._layout.name==="pre-paginated")return new Promise(function(n){n()});let t=e.window.getComputedStyle(e.content,null),i=(e.content.offsetHeight-(parseFloat(t.paddingTop)+parseFloat(t.paddingBottom)))*.95,r=parseFloat(t.paddingLeft)+parseFloat(t.paddingRight);return e.addStylesheetRules({img:{"max-width":(this._layout.columnWidth?this._layout.columnWidth-r+"px":"100%")+"!important","max-height":i+"px!important","object-fit":"contain","page-break-inside":"avoid","break-inside":"avoid","box-sizing":"border-box"},svg:{"max-width":(this._layout.columnWidth?this._layout.columnWidth-r+"px":"100%")+"!important","max-height":i+"px!important","page-break-inside":"avoid","break-inside":"avoid"}}),new Promise(function(n,s){setTimeout(function(){n()},1)})}getContents(){return this.manager?this.manager.getContents():[]}views(){return(this.manager?this.manager.views:void 0)||[]}handleLinks(e){e&&e.on(he.CONTENTS.LINK_CLICKED,t=>{let i=this.book.path.relative(t);this.display(i)})}injectStylesheet(e,t){let i=e.createElement("link");i.setAttribute("type","text/css"),i.setAttribute("rel","stylesheet"),i.setAttribute("href",this.settings.stylesheet),e.getElementsByTagName("head")[0].appendChild(i)}injectScript(e,t){let i=e.createElement("script");i.setAttribute("type","text/javascript"),i.setAttribute("src",this.settings.script),i.textContent=" ",e.getElementsByTagName("head")[0].appendChild(i)}injectIdentifier(e,t){let i=this.book.packaging.metadata.identifier,r=e.createElement("meta");r.setAttribute("name","dc.relation.ispartof"),i&&r.setAttribute("content",i),e.getElementsByTagName("head")[0].appendChild(r)}}St(kr.prototype);function Lt(y){throw new Error('Could not dynamically require "'+y+'". Please configure the dynamicRequireTargets or/and ignoreDynamicRequires option of @rollup/plugin-commonjs appropriately for this require call to work.')}var fr={exports:{}};/*! + +JSZip v3.10.1 - A JavaScript class for generating and reading zip files + + +(c) 2009-2016 Stuart Knightley +Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip/main/LICENSE.markdown. + +JSZip uses the library pako released under the MIT license : +https://github.com/nodeca/pako/blob/main/LICENSE +*/var Nn;function Ya(){return Nn||(Nn=1,(function(y,e){(function(t){y.exports=t()})(function(){return(function t(i,r,n){function s(h,d){if(!r[h]){if(!i[h]){var c=typeof Lt=="function"&≪if(!d&&c)return c(h,!0);if(a)return a(h,!0);var w=new Error("Cannot find module '"+h+"'");throw w.code="MODULE_NOT_FOUND",w}var m=r[h]={exports:{}};i[h][0].call(m.exports,function(N){var E=i[h][1][N];return s(E||N)},m,m.exports,t,i,r,n)}return r[h].exports}for(var a=typeof Lt=="function"&&Lt,u=0;u>2,N=(d&3)<<4|c>>4,E=D>1?(c&15)<<2|w>>6:64,f=D>2?w&63:64,h.push(a.charAt(m)+a.charAt(N)+a.charAt(E)+a.charAt(f));return h.join("")},r.decode=function(u){var h,d,c,w,m,N,E,f=0,v=0,g="data:";if(u.substr(0,g.length)===g)throw new Error("Invalid base64 input, it looks like a data url.");u=u.replace(/[^A-Za-z0-9+/=]/g,"");var D=u.length*3/4;if(u.charAt(u.length-1)===a.charAt(64)&&D--,u.charAt(u.length-2)===a.charAt(64)&&D--,D%1!==0)throw new Error("Invalid base64 input, bad content length.");var L;for(s.uint8array?L=new Uint8Array(D|0):L=new Array(D|0);f>4,d=(m&15)<<4|N>>2,c=(N&3)<<6|E,L[v++]=h,N!==64&&(L[v++]=d),E!==64&&(L[v++]=c);return L}},{"./support":30,"./utils":32}],2:[function(t,i,r){var n=t("./external"),s=t("./stream/DataWorker"),a=t("./stream/Crc32Probe"),u=t("./stream/DataLengthProbe");function h(d,c,w,m,N){this.compressedSize=d,this.uncompressedSize=c,this.crc32=w,this.compression=m,this.compressedContent=N}h.prototype={getContentWorker:function(){var d=new s(n.Promise.resolve(this.compressedContent)).pipe(this.compression.uncompressWorker()).pipe(new u("data_length")),c=this;return d.on("end",function(){if(this.streamInfo.data_length!==c.uncompressedSize)throw new Error("Bug : uncompressed data size mismatch")}),d},getCompressedWorker:function(){return new s(n.Promise.resolve(this.compressedContent)).withStreamInfo("compressedSize",this.compressedSize).withStreamInfo("uncompressedSize",this.uncompressedSize).withStreamInfo("crc32",this.crc32).withStreamInfo("compression",this.compression)}},h.createWorkerFrom=function(d,c,w){return d.pipe(new a).pipe(new u("uncompressedSize")).pipe(c.compressWorker(w)).pipe(new u("compressedSize")).withStreamInfo("compression",c)},i.exports=h},{"./external":6,"./stream/Crc32Probe":25,"./stream/DataLengthProbe":26,"./stream/DataWorker":27}],3:[function(t,i,r){var n=t("./stream/GenericWorker");r.STORE={magic:"\0\0",compressWorker:function(){return new n("STORE compression")},uncompressWorker:function(){return new n("STORE decompression")}},r.DEFLATE=t("./flate")},{"./flate":7,"./stream/GenericWorker":28}],4:[function(t,i,r){var n=t("./utils");function s(){for(var d,c=[],w=0;w<256;w++){d=w;for(var m=0;m<8;m++)d=d&1?3988292384^d>>>1:d>>>1;c[w]=d}return c}var a=s();function u(d,c,w,m){var N=a,E=m+w;d=d^-1;for(var f=m;f>>8^N[(d^c[f])&255];return d^-1}function h(d,c,w,m){var N=a,E=m+w;d=d^-1;for(var f=m;f>>8^N[(d^c.charCodeAt(f))&255];return d^-1}i.exports=function(c,w){if(typeof c>"u"||!c.length)return 0;var m=n.getTypeOf(c)!=="string";return m?u(w|0,c,c.length,0):h(w|0,c,c.length,0)}},{"./utils":32}],5:[function(t,i,r){r.base64=!1,r.binary=!1,r.dir=!1,r.createFolders=!0,r.date=null,r.compression=null,r.compressionOptions=null,r.comment=null,r.unixPermissions=null,r.dosPermissions=null},{}],6:[function(t,i,r){var n=null;typeof Promise<"u"?n=Promise:n=t("lie"),i.exports={Promise:n}},{lie:37}],7:[function(t,i,r){var n=typeof Uint8Array<"u"&&typeof Uint16Array<"u"&&typeof Uint32Array<"u",s=t("pako"),a=t("./utils"),u=t("./stream/GenericWorker"),h=n?"uint8array":"array";r.magic="\b\0";function d(c,w){u.call(this,"FlateWorker/"+c),this._pako=null,this._pakoAction=c,this._pakoOptions=w,this.meta={}}a.inherits(d,u),d.prototype.processChunk=function(c){this.meta=c.meta,this._pako===null&&this._createPako(),this._pako.push(a.transformTo(h,c.data),!1)},d.prototype.flush=function(){u.prototype.flush.call(this),this._pako===null&&this._createPako(),this._pako.push([],!0)},d.prototype.cleanUp=function(){u.prototype.cleanUp.call(this),this._pako=null},d.prototype._createPako=function(){this._pako=new s[this._pakoAction]({raw:!0,level:this._pakoOptions.level||-1});var c=this;this._pako.onData=function(w){c.push({data:w,meta:c.meta})}},r.compressWorker=function(c){return new d("Deflate",c)},r.uncompressWorker=function(){return new d("Inflate",{})}},{"./stream/GenericWorker":28,"./utils":32,pako:38}],8:[function(t,i,r){var n=t("../utils"),s=t("../stream/GenericWorker"),a=t("../utf8"),u=t("../crc32"),h=t("../signature"),d=function(v,g){var D="",L;for(L=0;L>>8;return D},c=function(v,g){var D=v;return v||(D=g?16893:33204),(D&65535)<<16},w=function(v){return(v||0)&63},m=function(v,g,D,L,b,S){var R=v.file,I=v.compression,k=S!==a.utf8encode,O=n.transformTo("string",S(R.name)),G=n.transformTo("string",a.utf8encode(R.name)),j=R.comment,Z=n.transformTo("string",S(j)),Y=n.transformTo("string",a.utf8encode(j)),X=G.length!==R.name.length,ie=Y.length!==j.length,K,$,ae="",pe="",ne="",ce=R.dir,Ce=R.date,ye={crc32:0,compressedSize:0,uncompressedSize:0};(!g||D)&&(ye.crc32=v.crc32,ye.compressedSize=v.compressedSize,ye.uncompressedSize=v.uncompressedSize);var Te=0;g&&(Te|=8),!k&&(X||ie)&&(Te|=2048);var We=0,Fe=0;ce&&(We|=16),b==="UNIX"?(Fe=798,We|=c(R.unixPermissions,ce)):(Fe=20,We|=w(R.dosPermissions)),K=Ce.getUTCHours(),K=K<<6,K=K|Ce.getUTCMinutes(),K=K<<5,K=K|Ce.getUTCSeconds()/2,$=Ce.getUTCFullYear()-1980,$=$<<4,$=$|Ce.getUTCMonth()+1,$=$<<5,$=$|Ce.getUTCDate(),X&&(pe=d(1,1)+d(u(O),4)+G,ae+="up"+d(pe.length,2)+pe),ie&&(ne=d(1,1)+d(u(Z),4)+Y,ae+="uc"+d(ne.length,2)+ne);var be="";be+=` +\0`,be+=d(Te,2),be+=I.magic,be+=d(K,2),be+=d($,2),be+=d(ye.crc32,4),be+=d(ye.compressedSize,4),be+=d(ye.uncompressedSize,4),be+=d(O.length,2),be+=d(ae.length,2);var Pe=h.LOCAL_FILE_HEADER+be+O+ae,Ue=h.CENTRAL_FILE_HEADER+d(Fe,2)+be+d(Z.length,2)+"\0\0\0\0"+d(We,4)+d(L,4)+O+ae+Z;return{fileRecord:Pe,dirRecord:Ue}},N=function(v,g,D,L,b){var S="",R=n.transformTo("string",b(L));return S=h.CENTRAL_DIRECTORY_END+"\0\0\0\0"+d(v,2)+d(v,2)+d(g,4)+d(D,4)+d(R.length,2)+R,S},E=function(v){var g="";return g=h.DATA_DESCRIPTOR+d(v.crc32,4)+d(v.compressedSize,4)+d(v.uncompressedSize,4),g};function f(v,g,D,L){s.call(this,"ZipFileWorker"),this.bytesWritten=0,this.zipComment=g,this.zipPlatform=D,this.encodeFileName=L,this.streamFiles=v,this.accumulate=!1,this.contentBuffer=[],this.dirRecords=[],this.currentSourceOffset=0,this.entriesCount=0,this.currentFile=null,this._sources=[]}n.inherits(f,s),f.prototype.push=function(v){var g=v.meta.percent||0,D=this.entriesCount,L=this._sources.length;this.accumulate?this.contentBuffer.push(v):(this.bytesWritten+=v.data.length,s.prototype.push.call(this,{data:v.data,meta:{currentFile:this.currentFile,percent:D?(g+100*(D-L-1))/D:100}}))},f.prototype.openedSource=function(v){this.currentSourceOffset=this.bytesWritten,this.currentFile=v.file.name;var g=this.streamFiles&&!v.file.dir;if(g){var D=m(v,g,!1,this.currentSourceOffset,this.zipPlatform,this.encodeFileName);this.push({data:D.fileRecord,meta:{percent:0}})}else this.accumulate=!0},f.prototype.closedSource=function(v){this.accumulate=!1;var g=this.streamFiles&&!v.file.dir,D=m(v,g,!0,this.currentSourceOffset,this.zipPlatform,this.encodeFileName);if(this.dirRecords.push(D.dirRecord),g)this.push({data:E(v),meta:{percent:100}});else for(this.push({data:D.fileRecord,meta:{percent:0}});this.contentBuffer.length;)this.push(this.contentBuffer.shift());this.currentFile=null},f.prototype.flush=function(){for(var v=this.bytesWritten,g=0;g"u")&&(O.binary=!G);var j=S instanceof d&&S.uncompressedSize===0;(j||O.dir||!S||S.length===0)&&(O.base64=!1,O.binary=!0,S="",O.compression="STORE",I="string");var Z=null;S instanceof d||S instanceof a?Z=S:m.isNode&&m.isStream(S)?Z=new N(b,S):Z=s.prepareContent(b,S,O.binary,O.optimizedBinaryString,O.base64);var Y=new c(b,Z,O);this.files[b]=Y},f=function(b){b.slice(-1)==="/"&&(b=b.substring(0,b.length-1));var S=b.lastIndexOf("/");return S>0?b.substring(0,S):""},v=function(b){return b.slice(-1)!=="/"&&(b+="/"),b},g=function(b,S){return S=typeof S<"u"?S:h.createFolders,b=v(b),this.files[b]||E.call(this,b,null,{dir:!0,createFolders:S}),this.files[b]};function D(b){return Object.prototype.toString.call(b)==="[object RegExp]"}var L={load:function(){throw new Error("This method has been removed in JSZip 3.0, please check the upgrade guide.")},forEach:function(b){var S,R,I;for(S in this.files)I=this.files[S],R=S.slice(this.root.length,S.length),R&&S.slice(0,this.root.length)===this.root&&b(R,I)},filter:function(b){var S=[];return this.forEach(function(R,I){b(R,I)&&S.push(I)}),S},file:function(b,S,R){if(arguments.length===1)if(D(b)){var I=b;return this.filter(function(O,G){return!G.dir&&I.test(O)})}else{var k=this.files[this.root+b];return k&&!k.dir?k:null}else b=this.root+b,E.call(this,b,S,R);return this},folder:function(b){if(!b)return this;if(D(b))return this.filter(function(k,O){return O.dir&&b.test(k)});var S=this.root+b,R=g.call(this,S),I=this.clone();return I.root=R.name,I},remove:function(b){b=this.root+b;var S=this.files[b];if(S||(b.slice(-1)!=="/"&&(b+="/"),S=this.files[b]),S&&!S.dir)delete this.files[b];else for(var R=this.filter(function(k,O){return O.name.slice(0,b.length)===b}),I=0;I=0;--m)if(this.data[m]===h&&this.data[m+1]===d&&this.data[m+2]===c&&this.data[m+3]===w)return m-this.zero;return-1},a.prototype.readAndCheckSignature=function(u){var h=u.charCodeAt(0),d=u.charCodeAt(1),c=u.charCodeAt(2),w=u.charCodeAt(3),m=this.readData(4);return h===m[0]&&d===m[1]&&c===m[2]&&w===m[3]},a.prototype.readData=function(u){if(this.checkOffset(u),u===0)return[];var h=this.data.slice(this.zero+this.index,this.zero+this.index+u);return this.index+=u,h},i.exports=a},{"../utils":32,"./DataReader":18}],18:[function(t,i,r){var n=t("../utils");function s(a){this.data=a,this.length=a.length,this.index=0,this.zero=0}s.prototype={checkOffset:function(a){this.checkIndex(this.index+a)},checkIndex:function(a){if(this.length=this.index;h--)u=(u<<8)+this.byteAt(h);return this.index+=a,u},readString:function(a){return n.transformTo("string",this.readData(a))},readData:function(){},lastIndexOfSignature:function(){},readAndCheckSignature:function(){},readDate:function(){var a=this.readInt(4);return new Date(Date.UTC((a>>25&127)+1980,(a>>21&15)-1,a>>16&31,a>>11&31,a>>5&63,(a&31)<<1))}},i.exports=s},{"../utils":32}],19:[function(t,i,r){var n=t("./Uint8ArrayReader"),s=t("../utils");function a(u){n.call(this,u)}s.inherits(a,n),a.prototype.readData=function(u){this.checkOffset(u);var h=this.data.slice(this.zero+this.index,this.zero+this.index+u);return this.index+=u,h},i.exports=a},{"../utils":32,"./Uint8ArrayReader":21}],20:[function(t,i,r){var n=t("./DataReader"),s=t("../utils");function a(u){n.call(this,u)}s.inherits(a,n),a.prototype.byteAt=function(u){return this.data.charCodeAt(this.zero+u)},a.prototype.lastIndexOfSignature=function(u){return this.data.lastIndexOf(u)-this.zero},a.prototype.readAndCheckSignature=function(u){var h=this.readData(4);return u===h},a.prototype.readData=function(u){this.checkOffset(u);var h=this.data.slice(this.zero+this.index,this.zero+this.index+u);return this.index+=u,h},i.exports=a},{"../utils":32,"./DataReader":18}],21:[function(t,i,r){var n=t("./ArrayReader"),s=t("../utils");function a(u){n.call(this,u)}s.inherits(a,n),a.prototype.readData=function(u){if(this.checkOffset(u),u===0)return new Uint8Array(0);var h=this.data.subarray(this.zero+this.index,this.zero+this.index+u);return this.index+=u,h},i.exports=a},{"../utils":32,"./ArrayReader":17}],22:[function(t,i,r){var n=t("../utils"),s=t("../support"),a=t("./ArrayReader"),u=t("./StringReader"),h=t("./NodeBufferReader"),d=t("./Uint8ArrayReader");i.exports=function(c){var w=n.getTypeOf(c);return n.checkSupport(w),w==="string"&&!s.uint8array?new u(c):w==="nodebuffer"?new h(c):s.uint8array?new d(n.transformTo("uint8array",c)):new a(n.transformTo("array",c))}},{"../support":30,"../utils":32,"./ArrayReader":17,"./NodeBufferReader":19,"./StringReader":20,"./Uint8ArrayReader":21}],23:[function(t,i,r){r.LOCAL_FILE_HEADER="PK",r.CENTRAL_FILE_HEADER="PK",r.CENTRAL_DIRECTORY_END="PK",r.ZIP64_CENTRAL_DIRECTORY_LOCATOR="PK\x07",r.ZIP64_CENTRAL_DIRECTORY_END="PK",r.DATA_DESCRIPTOR="PK\x07\b"},{}],24:[function(t,i,r){var n=t("./GenericWorker"),s=t("../utils");function a(u){n.call(this,"ConvertWorker to "+u),this.destType=u}s.inherits(a,n),a.prototype.processChunk=function(u){this.push({data:s.transformTo(this.destType,u.data),meta:u.meta})},i.exports=a},{"../utils":32,"./GenericWorker":28}],25:[function(t,i,r){var n=t("./GenericWorker"),s=t("../crc32"),a=t("../utils");function u(){n.call(this,"Crc32Probe"),this.withStreamInfo("crc32",0)}a.inherits(u,n),u.prototype.processChunk=function(h){this.streamInfo.crc32=s(h.data,this.streamInfo.crc32||0),this.push(h)},i.exports=u},{"../crc32":4,"../utils":32,"./GenericWorker":28}],26:[function(t,i,r){var n=t("../utils"),s=t("./GenericWorker");function a(u){s.call(this,"DataLengthProbe for "+u),this.propName=u,this.withStreamInfo(u,0)}n.inherits(a,s),a.prototype.processChunk=function(u){if(u){var h=this.streamInfo[this.propName]||0;this.streamInfo[this.propName]=h+u.data.length}s.prototype.processChunk.call(this,u)},i.exports=a},{"../utils":32,"./GenericWorker":28}],27:[function(t,i,r){var n=t("../utils"),s=t("./GenericWorker"),a=16*1024;function u(h){s.call(this,"DataWorker");var d=this;this.dataIsReady=!1,this.index=0,this.max=0,this.data=null,this.type="",this._tickScheduled=!1,h.then(function(c){d.dataIsReady=!0,d.data=c,d.max=c&&c.length||0,d.type=n.getTypeOf(c),d.isPaused||d._tickAndRepeat()},function(c){d.error(c)})}n.inherits(u,s),u.prototype.cleanUp=function(){s.prototype.cleanUp.call(this),this.data=null},u.prototype.resume=function(){return s.prototype.resume.call(this)?(!this._tickScheduled&&this.dataIsReady&&(this._tickScheduled=!0,n.delay(this._tickAndRepeat,[],this)),!0):!1},u.prototype._tickAndRepeat=function(){this._tickScheduled=!1,!(this.isPaused||this.isFinished)&&(this._tick(),this.isFinished||(n.delay(this._tickAndRepeat,[],this),this._tickScheduled=!0))},u.prototype._tick=function(){if(this.isPaused||this.isFinished)return!1;var h=a,d=null,c=Math.min(this.max,this.index+h);if(this.index>=this.max)return this.end();switch(this.type){case"string":d=this.data.substring(this.index,c);break;case"uint8array":d=this.data.subarray(this.index,c);break;case"array":case"nodebuffer":d=this.data.slice(this.index,c);break}return this.index=c,this.push({data:d,meta:{percent:this.max?this.index/this.max*100:0}})},i.exports=u},{"../utils":32,"./GenericWorker":28}],28:[function(t,i,r){function n(s){this.name=s||"default",this.streamInfo={},this.generatedError=null,this.extraStreamInfo={},this.isPaused=!0,this.isFinished=!1,this.isLocked=!1,this._listeners={data:[],end:[],error:[]},this.previous=null}n.prototype={push:function(s){this.emit("data",s)},end:function(){if(this.isFinished)return!1;this.flush();try{this.emit("end"),this.cleanUp(),this.isFinished=!0}catch(s){this.emit("error",s)}return!0},error:function(s){return this.isFinished?!1:(this.isPaused?this.generatedError=s:(this.isFinished=!0,this.emit("error",s),this.previous&&this.previous.error(s),this.cleanUp()),!0)},on:function(s,a){return this._listeners[s].push(a),this},cleanUp:function(){this.streamInfo=this.generatedError=this.extraStreamInfo=null,this._listeners=[]},emit:function(s,a){if(this._listeners[s])for(var u=0;u "+s:s}},i.exports=n},{}],29:[function(t,i,r){var n=t("../utils"),s=t("./ConvertWorker"),a=t("./GenericWorker"),u=t("../base64"),h=t("../support"),d=t("../external"),c=null;if(h.nodestream)try{c=t("../nodejs/NodejsStreamOutputAdapter")}catch{}function w(f,v,g){switch(f){case"blob":return n.newBlob(n.transformTo("arraybuffer",v),g);case"base64":return u.encode(v);default:return n.transformTo(f,v)}}function m(f,v){var g,D=0,L=null,b=0;for(g=0;g"u")r.blob=!1;else{var n=new ArrayBuffer(0);try{r.blob=new Blob([n],{type:"application/zip"}).size===0}catch{try{var s=self.BlobBuilder||self.WebKitBlobBuilder||self.MozBlobBuilder||self.MSBlobBuilder,a=new s;a.append(n),r.blob=a.getBlob("application/zip").size===0}catch{r.blob=!1}}}try{r.nodestream=!!t("readable-stream").Readable}catch{r.nodestream=!1}},{"readable-stream":16}],31:[function(t,i,r){for(var n=t("./utils"),s=t("./support"),a=t("./nodejsUtils"),u=t("./stream/GenericWorker"),h=new Array(256),d=0;d<256;d++)h[d]=d>=252?6:d>=248?5:d>=240?4:d>=224?3:d>=192?2:1;h[254]=h[254]=1;var c=function(f){var v,g,D,L,b,S=f.length,R=0;for(L=0;L>>6,v[b++]=128|g&63):g<65536?(v[b++]=224|g>>>12,v[b++]=128|g>>>6&63,v[b++]=128|g&63):(v[b++]=240|g>>>18,v[b++]=128|g>>>12&63,v[b++]=128|g>>>6&63,v[b++]=128|g&63);return v},w=function(f,v){var g;for(v=v||f.length,v>f.length&&(v=f.length),g=v-1;g>=0&&(f[g]&192)===128;)g--;return g<0||g===0?v:g+h[f[g]]>v?g:v},m=function(f){var v,g,D,L,b=f.length,S=new Array(b*2);for(g=0,v=0;v4){S[g++]=65533,v+=L-1;continue}for(D&=L===2?31:L===3?15:7;L>1&&v1){S[g++]=65533;continue}D<65536?S[g++]=D:(D-=65536,S[g++]=55296|D>>10&1023,S[g++]=56320|D&1023)}return S.length!==g&&(S.subarray?S=S.subarray(0,g):S.length=g),n.applyFromCharCode(S)};r.utf8encode=function(v){return s.nodebuffer?a.newBufferFrom(v,"utf-8"):c(v)},r.utf8decode=function(v){return s.nodebuffer?n.transformTo("nodebuffer",v).toString("utf-8"):(v=n.transformTo(s.uint8array?"uint8array":"array",v),m(v))};function N(){u.call(this,"utf-8 decode"),this.leftOver=null}n.inherits(N,u),N.prototype.processChunk=function(f){var v=n.transformTo(s.uint8array?"uint8array":"array",f.data);if(this.leftOver&&this.leftOver.length){if(s.uint8array){var g=v;v=new Uint8Array(g.length+this.leftOver.length),v.set(this.leftOver,0),v.set(g,this.leftOver.length)}else v=this.leftOver.concat(v);this.leftOver=null}var D=w(v),L=v;D!==v.length&&(s.uint8array?(L=v.subarray(0,D),this.leftOver=v.subarray(D,v.length)):(L=v.slice(0,D),this.leftOver=v.slice(D,v.length))),this.push({data:r.utf8decode(L),meta:f.meta})},N.prototype.flush=function(){this.leftOver&&this.leftOver.length&&(this.push({data:r.utf8decode(this.leftOver),meta:{}}),this.leftOver=null)},r.Utf8DecodeWorker=N;function E(){u.call(this,"utf-8 encode")}n.inherits(E,u),E.prototype.processChunk=function(f){this.push({data:r.utf8encode(f.data),meta:f.meta})},r.Utf8EncodeWorker=E},{"./nodejsUtils":14,"./stream/GenericWorker":28,"./support":30,"./utils":32}],32:[function(t,i,r){var n=t("./support"),s=t("./base64"),a=t("./nodejsUtils"),u=t("./external");t("setimmediate");function h(f){var v=null;return n.uint8array?v=new Uint8Array(f.length):v=new Array(f.length),c(f,v)}r.newBlob=function(f,v){r.checkSupport("blob");try{return new Blob([f],{type:v})}catch{try{var g=self.BlobBuilder||self.WebKitBlobBuilder||self.MozBlobBuilder||self.MSBlobBuilder,D=new g;return D.append(f),D.getBlob(v)}catch{throw new Error("Bug : can't construct the Blob.")}}};function d(f){return f}function c(f,v){for(var g=0;g1;)try{return w.stringifyByChunk(f,g,v)}catch{v=Math.floor(v/2)}return w.stringifyByChar(f)}r.applyFromCharCode=m;function N(f,v){for(var g=0;g"u"&&(f[g]=arguments[v][g]);return f},r.prepareContent=function(f,v,g,D,L){var b=u.Promise.resolve(v).then(function(S){var R=n.blob&&(S instanceof Blob||["[object File]","[object Blob]"].indexOf(Object.prototype.toString.call(S))!==-1);return R&&typeof FileReader<"u"?new u.Promise(function(I,k){var O=new FileReader;O.onload=function(G){I(G.target.result)},O.onerror=function(G){k(G.target.error)},O.readAsArrayBuffer(S)}):S});return b.then(function(S){var R=r.getTypeOf(S);return R?(R==="arraybuffer"?S=r.transformTo("uint8array",S):R==="string"&&(L?S=s.decode(S):g&&D!==!0&&(S=h(S))),S):u.Promise.reject(new Error("Can't read the data of '"+f+"'. Is it in a supported JavaScript type (String, Blob, ArrayBuffer, etc) ?"))})}},{"./base64":1,"./external":6,"./nodejsUtils":14,"./support":30,setimmediate:54}],33:[function(t,i,r){var n=t("./reader/readerFor"),s=t("./utils"),a=t("./signature"),u=t("./zipEntry"),h=t("./support");function d(c){this.files=[],this.loadOptions=c}d.prototype={checkSignature:function(c){if(!this.reader.readAndCheckSignature(c)){this.reader.index-=4;var w=this.reader.readString(4);throw new Error("Corrupted zip or bug: unexpected signature ("+s.pretty(w)+", expected "+s.pretty(c)+")")}},isSignature:function(c,w){var m=this.reader.index;this.reader.setIndex(c);var N=this.reader.readString(4),E=N===w;return this.reader.setIndex(m),E},readBlockEndOfCentral:function(){this.diskNumber=this.reader.readInt(2),this.diskWithCentralDirStart=this.reader.readInt(2),this.centralDirRecordsOnThisDisk=this.reader.readInt(2),this.centralDirRecords=this.reader.readInt(2),this.centralDirSize=this.reader.readInt(4),this.centralDirOffset=this.reader.readInt(4),this.zipCommentLength=this.reader.readInt(2);var c=this.reader.readData(this.zipCommentLength),w=h.uint8array?"uint8array":"array",m=s.transformTo(w,c);this.zipComment=this.loadOptions.decodeFileName(m)},readBlockZip64EndOfCentral:function(){this.zip64EndOfCentralSize=this.reader.readInt(8),this.reader.skip(4),this.diskNumber=this.reader.readInt(4),this.diskWithCentralDirStart=this.reader.readInt(4),this.centralDirRecordsOnThisDisk=this.reader.readInt(8),this.centralDirRecords=this.reader.readInt(8),this.centralDirSize=this.reader.readInt(8),this.centralDirOffset=this.reader.readInt(8),this.zip64ExtensibleData={};for(var c=this.zip64EndOfCentralSize-44,w=0,m,N,E;w1)throw new Error("Multi-volumes zip are not supported")},readLocalFiles:function(){var c,w;for(c=0;c0)this.isSignature(m,a.CENTRAL_FILE_HEADER)||(this.reader.zero=E);else if(E<0)throw new Error("Corrupted zip: missing "+Math.abs(E)+" bytes.")},prepareReader:function(c){this.reader=n(c)},load:function(c){this.prepareReader(c),this.readEndOfCentral(),this.readCentralDir(),this.readLocalFiles()}},i.exports=d},{"./reader/readerFor":22,"./signature":23,"./support":30,"./utils":32,"./zipEntry":34}],34:[function(t,i,r){var n=t("./reader/readerFor"),s=t("./utils"),a=t("./compressedObject"),u=t("./crc32"),h=t("./utf8"),d=t("./compressions"),c=t("./support"),w=0,m=3,N=function(f){for(var v in d)if(Object.prototype.hasOwnProperty.call(d,v)&&d[v].magic===f)return d[v];return null};function E(f,v){this.options=f,this.loadOptions=v}E.prototype={isEncrypted:function(){return(this.bitFlag&1)===1},useUTF8:function(){return(this.bitFlag&2048)===2048},readLocalPart:function(f){var v,g;if(f.skip(22),this.fileNameLength=f.readInt(2),g=f.readInt(2),this.fileName=f.readData(this.fileNameLength),f.skip(g),this.compressedSize===-1||this.uncompressedSize===-1)throw new Error("Bug or corrupted zip : didn't get enough information from the central directory (compressedSize === -1 || uncompressedSize === -1)");if(v=N(this.compressionMethod),v===null)throw new Error("Corrupted zip : compression "+s.pretty(this.compressionMethod)+" unknown (inner file : "+s.transformTo("string",this.fileName)+")");this.decompressed=new a(this.compressedSize,this.uncompressedSize,this.crc32,v,f.readData(this.compressedSize))},readCentralPart:function(f){this.versionMadeBy=f.readInt(2),f.skip(2),this.bitFlag=f.readInt(2),this.compressionMethod=f.readString(2),this.date=f.readDate(),this.crc32=f.readInt(4),this.compressedSize=f.readInt(4),this.uncompressedSize=f.readInt(4);var v=f.readInt(2);if(this.extraFieldsLength=f.readInt(2),this.fileCommentLength=f.readInt(2),this.diskNumberStart=f.readInt(2),this.internalFileAttributes=f.readInt(2),this.externalFileAttributes=f.readInt(4),this.localHeaderOffset=f.readInt(4),this.isEncrypted())throw new Error("Encrypted zip are not supported");f.skip(v),this.readExtraFields(f),this.parseZIP64ExtraField(f),this.fileComment=f.readData(this.fileCommentLength)},processAttributes:function(){this.unixPermissions=null,this.dosPermissions=null;var f=this.versionMadeBy>>8;this.dir=!!(this.externalFileAttributes&16),f===w&&(this.dosPermissions=this.externalFileAttributes&63),f===m&&(this.unixPermissions=this.externalFileAttributes>>16&65535),!this.dir&&this.fileNameStr.slice(-1)==="/"&&(this.dir=!0)},parseZIP64ExtraField:function(){if(this.extraFields[1]){var f=n(this.extraFields[1].value);this.uncompressedSize===s.MAX_VALUE_32BITS&&(this.uncompressedSize=f.readInt(8)),this.compressedSize===s.MAX_VALUE_32BITS&&(this.compressedSize=f.readInt(8)),this.localHeaderOffset===s.MAX_VALUE_32BITS&&(this.localHeaderOffset=f.readInt(8)),this.diskNumberStart===s.MAX_VALUE_32BITS&&(this.diskNumberStart=f.readInt(4))}},readExtraFields:function(f){var v=f.index+this.extraFieldsLength,g,D,L;for(this.extraFields||(this.extraFields={});f.index+40?I.windowBits=-I.windowBits:I.gzip&&I.windowBits>0&&I.windowBits<16&&(I.windowBits+=16),this.err=0,this.msg="",this.ended=!1,this.chunks=[],this.strm=new h,this.strm.avail_out=0;var k=n.deflateInit2(this.strm,I.level,I.method,I.windowBits,I.memLevel,I.strategy);if(k!==m)throw new Error(u[k]);if(I.header&&n.deflateSetHeader(this.strm,I.header),I.dictionary){var O;if(typeof I.dictionary=="string"?O=a.string2buf(I.dictionary):d.call(I.dictionary)==="[object ArrayBuffer]"?O=new Uint8Array(I.dictionary):O=I.dictionary,k=n.deflateSetDictionary(this.strm,O),k!==m)throw new Error(u[k]);this._dict_set=!0}}D.prototype.push=function(R,I){var k=this.strm,O=this.options.chunkSize,G,j;if(this.ended)return!1;j=I===~~I?I:I===!0?w:c,typeof R=="string"?k.input=a.string2buf(R):d.call(R)==="[object ArrayBuffer]"?k.input=new Uint8Array(R):k.input=R,k.next_in=0,k.avail_in=k.input.length;do{if(k.avail_out===0&&(k.output=new s.Buf8(O),k.next_out=0,k.avail_out=O),G=n.deflate(k,j),G!==N&&G!==m)return this.onEnd(G),this.ended=!0,!1;(k.avail_out===0||k.avail_in===0&&(j===w||j===E))&&(this.options.to==="string"?this.onData(a.buf2binstring(s.shrinkBuf(k.output,k.next_out))):this.onData(s.shrinkBuf(k.output,k.next_out)))}while((k.avail_in>0||k.avail_out===0)&&G!==N);return j===w?(G=n.deflateEnd(this.strm),this.onEnd(G),this.ended=!0,G===m):(j===E&&(this.onEnd(m),k.avail_out=0),!0)},D.prototype.onData=function(R){this.chunks.push(R)},D.prototype.onEnd=function(R){R===m&&(this.options.to==="string"?this.result=this.chunks.join(""):this.result=s.flattenChunks(this.chunks)),this.chunks=[],this.err=R,this.msg=this.strm.msg};function L(R,I){var k=new D(I);if(k.push(R,!0),k.err)throw k.msg||u[k.err];return k.result}function b(R,I){return I=I||{},I.raw=!0,L(R,I)}function S(R,I){return I=I||{},I.gzip=!0,L(R,I)}r.Deflate=D,r.deflate=L,r.deflateRaw=b,r.gzip=S},{"./utils/common":41,"./utils/strings":42,"./zlib/deflate":46,"./zlib/messages":51,"./zlib/zstream":53}],40:[function(t,i,r){var n=t("./zlib/inflate"),s=t("./utils/common"),a=t("./utils/strings"),u=t("./zlib/constants"),h=t("./zlib/messages"),d=t("./zlib/zstream"),c=t("./zlib/gzheader"),w=Object.prototype.toString;function m(f){if(!(this instanceof m))return new m(f);this.options=s.assign({chunkSize:16384,windowBits:0,to:""},f||{});var v=this.options;v.raw&&v.windowBits>=0&&v.windowBits<16&&(v.windowBits=-v.windowBits,v.windowBits===0&&(v.windowBits=-15)),v.windowBits>=0&&v.windowBits<16&&!(f&&f.windowBits)&&(v.windowBits+=32),v.windowBits>15&&v.windowBits<48&&(v.windowBits&15)===0&&(v.windowBits|=15),this.err=0,this.msg="",this.ended=!1,this.chunks=[],this.strm=new d,this.strm.avail_out=0;var g=n.inflateInit2(this.strm,v.windowBits);if(g!==u.Z_OK)throw new Error(h[g]);this.header=new c,n.inflateGetHeader(this.strm,this.header)}m.prototype.push=function(f,v){var g=this.strm,D=this.options.chunkSize,L=this.options.dictionary,b,S,R,I,k,O,G=!1;if(this.ended)return!1;S=v===~~v?v:v===!0?u.Z_FINISH:u.Z_NO_FLUSH,typeof f=="string"?g.input=a.binstring2buf(f):w.call(f)==="[object ArrayBuffer]"?g.input=new Uint8Array(f):g.input=f,g.next_in=0,g.avail_in=g.input.length;do{if(g.avail_out===0&&(g.output=new s.Buf8(D),g.next_out=0,g.avail_out=D),b=n.inflate(g,u.Z_NO_FLUSH),b===u.Z_NEED_DICT&&L&&(typeof L=="string"?O=a.string2buf(L):w.call(L)==="[object ArrayBuffer]"?O=new Uint8Array(L):O=L,b=n.inflateSetDictionary(this.strm,O)),b===u.Z_BUF_ERROR&&G===!0&&(b=u.Z_OK,G=!1),b!==u.Z_STREAM_END&&b!==u.Z_OK)return this.onEnd(b),this.ended=!0,!1;g.next_out&&(g.avail_out===0||b===u.Z_STREAM_END||g.avail_in===0&&(S===u.Z_FINISH||S===u.Z_SYNC_FLUSH))&&(this.options.to==="string"?(R=a.utf8border(g.output,g.next_out),I=g.next_out-R,k=a.buf2string(g.output,R),g.next_out=I,g.avail_out=D-I,I&&s.arraySet(g.output,g.output,R,I,0),this.onData(k)):this.onData(s.shrinkBuf(g.output,g.next_out))),g.avail_in===0&&g.avail_out===0&&(G=!0)}while((g.avail_in>0||g.avail_out===0)&&b!==u.Z_STREAM_END);return b===u.Z_STREAM_END&&(S=u.Z_FINISH),S===u.Z_FINISH?(b=n.inflateEnd(this.strm),this.onEnd(b),this.ended=!0,b===u.Z_OK):(S===u.Z_SYNC_FLUSH&&(this.onEnd(u.Z_OK),g.avail_out=0),!0)},m.prototype.onData=function(f){this.chunks.push(f)},m.prototype.onEnd=function(f){f===u.Z_OK&&(this.options.to==="string"?this.result=this.chunks.join(""):this.result=s.flattenChunks(this.chunks)),this.chunks=[],this.err=f,this.msg=this.strm.msg};function N(f,v){var g=new m(v);if(g.push(f,!0),g.err)throw g.msg||h[g.err];return g.result}function E(f,v){return v=v||{},v.raw=!0,N(f,v)}r.Inflate=m,r.inflate=N,r.inflateRaw=E,r.ungzip=N},{"./utils/common":41,"./utils/strings":42,"./zlib/constants":44,"./zlib/gzheader":47,"./zlib/inflate":49,"./zlib/messages":51,"./zlib/zstream":53}],41:[function(t,i,r){var n=typeof Uint8Array<"u"&&typeof Uint16Array<"u"&&typeof Int32Array<"u";r.assign=function(u){for(var h=Array.prototype.slice.call(arguments,1);h.length;){var d=h.shift();if(d){if(typeof d!="object")throw new TypeError(d+"must be non-object");for(var c in d)d.hasOwnProperty(c)&&(u[c]=d[c])}}return u},r.shrinkBuf=function(u,h){return u.length===h?u:u.subarray?u.subarray(0,h):(u.length=h,u)};var s={arraySet:function(u,h,d,c,w){if(h.subarray&&u.subarray){u.set(h.subarray(d,d+c),w);return}for(var m=0;m=252?6:h>=248?5:h>=240?4:h>=224?3:h>=192?2:1;u[254]=u[254]=1,r.string2buf=function(c){var w,m,N,E,f,v=c.length,g=0;for(E=0;E>>6,w[f++]=128|m&63):m<65536?(w[f++]=224|m>>>12,w[f++]=128|m>>>6&63,w[f++]=128|m&63):(w[f++]=240|m>>>18,w[f++]=128|m>>>12&63,w[f++]=128|m>>>6&63,w[f++]=128|m&63);return w};function d(c,w){if(w<65537&&(c.subarray&&a||!c.subarray&&s))return String.fromCharCode.apply(null,n.shrinkBuf(c,w));for(var m="",N=0;N4){g[N++]=65533,m+=f-1;continue}for(E&=f===2?31:f===3?15:7;f>1&&m1){g[N++]=65533;continue}E<65536?g[N++]=E:(E-=65536,g[N++]=55296|E>>10&1023,g[N++]=56320|E&1023)}return d(g,N)},r.utf8border=function(c,w){var m;for(w=w||c.length,w>c.length&&(w=c.length),m=w-1;m>=0&&(c[m]&192)===128;)m--;return m<0||m===0?w:m+u[c[m]]>w?m:w}},{"./common":41}],43:[function(t,i,r){function n(s,a,u,h){for(var d=s&65535|0,c=s>>>16&65535|0,w=0;u!==0;){w=u>2e3?2e3:u,u-=w;do d=d+a[h++]|0,c=c+d|0;while(--w);d%=65521,c%=65521}return d|c<<16|0}i.exports=n},{}],44:[function(t,i,r){i.exports={Z_NO_FLUSH:0,Z_PARTIAL_FLUSH:1,Z_SYNC_FLUSH:2,Z_FULL_FLUSH:3,Z_FINISH:4,Z_BLOCK:5,Z_TREES:6,Z_OK:0,Z_STREAM_END:1,Z_NEED_DICT:2,Z_ERRNO:-1,Z_STREAM_ERROR:-2,Z_DATA_ERROR:-3,Z_BUF_ERROR:-5,Z_NO_COMPRESSION:0,Z_BEST_SPEED:1,Z_BEST_COMPRESSION:9,Z_DEFAULT_COMPRESSION:-1,Z_FILTERED:1,Z_HUFFMAN_ONLY:2,Z_RLE:3,Z_FIXED:4,Z_DEFAULT_STRATEGY:0,Z_BINARY:0,Z_TEXT:1,Z_UNKNOWN:2,Z_DEFLATED:8}},{}],45:[function(t,i,r){function n(){for(var u,h=[],d=0;d<256;d++){u=d;for(var c=0;c<8;c++)u=u&1?3988292384^u>>>1:u>>>1;h[d]=u}return h}var s=n();function a(u,h,d,c){var w=s,m=c+d;u^=-1;for(var N=c;N>>8^w[(u^h[N])&255];return u^-1}i.exports=a},{}],46:[function(t,i,r){var n=t("../utils/common"),s=t("./trees"),a=t("./adler32"),u=t("./crc32"),h=t("./messages"),d=0,c=1,w=3,m=4,N=5,E=0,f=1,v=-2,g=-3,D=-5,L=-1,b=1,S=2,R=3,I=4,k=0,O=2,G=8,j=9,Z=15,Y=8,X=29,ie=256,K=ie+1+X,$=30,ae=19,pe=2*K+1,ne=15,ce=3,Ce=258,ye=Ce+ce+1,Te=32,We=42,Fe=69,be=73,Pe=91,Ue=103,xe=113,ke=666,ve=1,Ie=2,He=3,ze=4,Oe=3;function Ge(l,M){return l.msg=h[M],M}function tt(l){return(l<<1)-(l>4?9:0)}function Je(l){for(var M=l.length;--M>=0;)l[M]=0}function Ee(l){var M=l.state,H=M.pending;H>l.avail_out&&(H=l.avail_out),H!==0&&(n.arraySet(l.output,M.pending_buf,M.pending_out,H,l.next_out),l.next_out+=H,M.pending_out+=H,l.total_out+=H,l.avail_out-=H,M.pending-=H,M.pending===0&&(M.pending_out=0))}function De(l,M){s._tr_flush_block(l,l.block_start>=0?l.block_start:-1,l.strstart-l.block_start,M),l.block_start=l.strstart,Ee(l.strm)}function _e(l,M){l.pending_buf[l.pending++]=M}function nt(l,M){l.pending_buf[l.pending++]=M>>>8&255,l.pending_buf[l.pending++]=M&255}function pt(l,M,H,_){var B=l.avail_in;return B>_&&(B=_),B===0?0:(l.avail_in-=B,n.arraySet(M,l.input,l.next_in,B,H),l.state.wrap===1?l.adler=a(l.adler,M,B,H):l.state.wrap===2&&(l.adler=u(l.adler,M,B,H)),l.next_in+=B,l.total_in+=B,B)}function lt(l,M){var H=l.max_chain_length,_=l.strstart,B,V,de=l.prev_length,ue=l.nice_match,le=l.strstart>l.w_size-ye?l.strstart-(l.w_size-ye):0,Ae=l.window,Xe=l.w_mask,Me=l.prev,Se=l.strstart+Ce,Ne=Ae[_+de-1],Qe=Ae[_+de];l.prev_length>=l.good_match&&(H>>=2),ue>l.lookahead&&(ue=l.lookahead);do if(B=M,!(Ae[B+de]!==Qe||Ae[B+de-1]!==Ne||Ae[B]!==Ae[_]||Ae[++B]!==Ae[_+1])){_+=2,B++;do;while(Ae[++_]===Ae[++B]&&Ae[++_]===Ae[++B]&&Ae[++_]===Ae[++B]&&Ae[++_]===Ae[++B]&&Ae[++_]===Ae[++B]&&Ae[++_]===Ae[++B]&&Ae[++_]===Ae[++B]&&Ae[++_]===Ae[++B]&&_de){if(l.match_start=M,de=V,V>=ue)break;Ne=Ae[_+de-1],Qe=Ae[_+de]}}while((M=Me[M&Xe])>le&&--H!==0);return de<=l.lookahead?de:l.lookahead}function it(l){var M=l.w_size,H,_,B,V,de;do{if(V=l.window_size-l.lookahead-l.strstart,l.strstart>=M+(M-ye)){n.arraySet(l.window,l.window,M,M,0),l.match_start-=M,l.strstart-=M,l.block_start-=M,_=l.hash_size,H=_;do B=l.head[--H],l.head[H]=B>=M?B-M:0;while(--_);_=M,H=_;do B=l.prev[--H],l.prev[H]=B>=M?B-M:0;while(--_);V+=M}if(l.strm.avail_in===0)break;if(_=pt(l.strm,l.window,l.strstart+l.lookahead,V),l.lookahead+=_,l.lookahead+l.insert>=ce)for(de=l.strstart-l.insert,l.ins_h=l.window[de],l.ins_h=(l.ins_h<l.pending_buf_size-5&&(H=l.pending_buf_size-5);;){if(l.lookahead<=1){if(it(l),l.lookahead===0&&M===d)return ve;if(l.lookahead===0)break}l.strstart+=l.lookahead,l.lookahead=0;var _=l.block_start+H;if((l.strstart===0||l.strstart>=_)&&(l.lookahead=l.strstart-_,l.strstart=_,De(l,!1),l.strm.avail_out===0)||l.strstart-l.block_start>=l.w_size-ye&&(De(l,!1),l.strm.avail_out===0))return ve}return l.insert=0,M===m?(De(l,!0),l.strm.avail_out===0?He:ze):(l.strstart>l.block_start&&(De(l,!1),l.strm.avail_out===0),ve)}function ft(l,M){for(var H,_;;){if(l.lookahead=ce&&(l.ins_h=(l.ins_h<=ce)if(_=s._tr_tally(l,l.strstart-l.match_start,l.match_length-ce),l.lookahead-=l.match_length,l.match_length<=l.max_lazy_match&&l.lookahead>=ce){l.match_length--;do l.strstart++,l.ins_h=(l.ins_h<=ce&&(l.ins_h=(l.ins_h<4096)&&(l.match_length=ce-1)),l.prev_length>=ce&&l.match_length<=l.prev_length){B=l.strstart+l.lookahead-ce,_=s._tr_tally(l,l.strstart-1-l.prev_match,l.prev_length-ce),l.lookahead-=l.prev_length-1,l.prev_length-=2;do++l.strstart<=B&&(l.ins_h=(l.ins_h<=ce&&l.strstart>0&&(B=l.strstart-1,_=de[B],_===de[++B]&&_===de[++B]&&_===de[++B])){V=l.strstart+Ce;do;while(_===de[++B]&&_===de[++B]&&_===de[++B]&&_===de[++B]&&_===de[++B]&&_===de[++B]&&_===de[++B]&&_===de[++B]&&Bl.lookahead&&(l.match_length=l.lookahead)}if(l.match_length>=ce?(H=s._tr_tally(l,1,l.match_length-ce),l.lookahead-=l.match_length,l.strstart+=l.match_length,l.match_length=0):(H=s._tr_tally(l,0,l.window[l.strstart]),l.lookahead--,l.strstart++),H&&(De(l,!1),l.strm.avail_out===0))return ve}return l.insert=0,M===m?(De(l,!0),l.strm.avail_out===0?He:ze):l.last_lit&&(De(l,!1),l.strm.avail_out===0)?ve:Ie}function ht(l,M){for(var H;;){if(l.lookahead===0&&(it(l),l.lookahead===0)){if(M===d)return ve;break}if(l.match_length=0,H=s._tr_tally(l,0,l.window[l.strstart]),l.lookahead--,l.strstart++,H&&(De(l,!1),l.strm.avail_out===0))return ve}return l.insert=0,M===m?(De(l,!0),l.strm.avail_out===0?He:ze):l.last_lit&&(De(l,!1),l.strm.avail_out===0)?ve:Ie}function Ye(l,M,H,_,B){this.good_length=l,this.max_lazy=M,this.nice_length=H,this.max_chain=_,this.func=B}var ot;ot=[new Ye(0,0,0,0,vt),new Ye(4,4,8,4,ft),new Ye(4,5,16,8,ft),new Ye(4,6,32,32,ft),new Ye(4,4,16,16,at),new Ye(8,16,32,32,at),new Ye(8,16,128,128,at),new Ye(8,32,128,256,at),new Ye(32,128,258,1024,at),new Ye(32,258,258,4096,at)];function wt(l){l.window_size=2*l.w_size,Je(l.head),l.max_lazy_match=ot[l.level].max_lazy,l.good_match=ot[l.level].good_length,l.nice_match=ot[l.level].nice_length,l.max_chain_length=ot[l.level].max_chain,l.strstart=0,l.block_start=0,l.lookahead=0,l.insert=0,l.match_length=l.prev_length=ce-1,l.match_available=0,l.ins_h=0}function C(){this.strm=null,this.status=0,this.pending_buf=null,this.pending_buf_size=0,this.pending_out=0,this.pending=0,this.wrap=0,this.gzhead=null,this.gzindex=0,this.method=G,this.last_flush=-1,this.w_size=0,this.w_bits=0,this.w_mask=0,this.window=null,this.window_size=0,this.prev=null,this.head=null,this.ins_h=0,this.hash_size=0,this.hash_bits=0,this.hash_mask=0,this.hash_shift=0,this.block_start=0,this.match_length=0,this.prev_match=0,this.match_available=0,this.strstart=0,this.match_start=0,this.lookahead=0,this.prev_length=0,this.max_chain_length=0,this.max_lazy_match=0,this.level=0,this.strategy=0,this.good_match=0,this.nice_match=0,this.dyn_ltree=new n.Buf16(pe*2),this.dyn_dtree=new n.Buf16((2*$+1)*2),this.bl_tree=new n.Buf16((2*ae+1)*2),Je(this.dyn_ltree),Je(this.dyn_dtree),Je(this.bl_tree),this.l_desc=null,this.d_desc=null,this.bl_desc=null,this.bl_count=new n.Buf16(ne+1),this.heap=new n.Buf16(2*K+1),Je(this.heap),this.heap_len=0,this.heap_max=0,this.depth=new n.Buf16(2*K+1),Je(this.depth),this.l_buf=0,this.lit_bufsize=0,this.last_lit=0,this.d_buf=0,this.opt_len=0,this.static_len=0,this.matches=0,this.insert=0,this.bi_buf=0,this.bi_valid=0}function Q(l){var M;return!l||!l.state?Ge(l,v):(l.total_in=l.total_out=0,l.data_type=O,M=l.state,M.pending=0,M.pending_out=0,M.wrap<0&&(M.wrap=-M.wrap),M.status=M.wrap?We:xe,l.adler=M.wrap===2?0:1,M.last_flush=d,s._tr_init(M),E)}function te(l){var M=Q(l);return M===E&&wt(l.state),M}function fe(l,M){return!l||!l.state||l.state.wrap!==2?v:(l.state.gzhead=M,E)}function F(l,M,H,_,B,V){if(!l)return v;var de=1;if(M===L&&(M=6),_<0?(de=0,_=-_):_>15&&(de=2,_-=16),B<1||B>j||H!==G||_<8||_>15||M<0||M>9||V<0||V>I)return Ge(l,v);_===8&&(_=9);var ue=new C;return l.state=ue,ue.strm=l,ue.wrap=de,ue.gzhead=null,ue.w_bits=_,ue.w_size=1<N||M<0)return l?Ge(l,v):v;if(_=l.state,!l.output||!l.input&&l.avail_in!==0||_.status===ke&&M!==m)return Ge(l,l.avail_out===0?D:v);if(_.strm=l,H=_.last_flush,_.last_flush=M,_.status===We)if(_.wrap===2)l.adler=0,_e(_,31),_e(_,139),_e(_,8),_.gzhead?(_e(_,(_.gzhead.text?1:0)+(_.gzhead.hcrc?2:0)+(_.gzhead.extra?4:0)+(_.gzhead.name?8:0)+(_.gzhead.comment?16:0)),_e(_,_.gzhead.time&255),_e(_,_.gzhead.time>>8&255),_e(_,_.gzhead.time>>16&255),_e(_,_.gzhead.time>>24&255),_e(_,_.level===9?2:_.strategy>=S||_.level<2?4:0),_e(_,_.gzhead.os&255),_.gzhead.extra&&_.gzhead.extra.length&&(_e(_,_.gzhead.extra.length&255),_e(_,_.gzhead.extra.length>>8&255)),_.gzhead.hcrc&&(l.adler=u(l.adler,_.pending_buf,_.pending,0)),_.gzindex=0,_.status=Fe):(_e(_,0),_e(_,0),_e(_,0),_e(_,0),_e(_,0),_e(_,_.level===9?2:_.strategy>=S||_.level<2?4:0),_e(_,Oe),_.status=xe);else{var de=G+(_.w_bits-8<<4)<<8,ue=-1;_.strategy>=S||_.level<2?ue=0:_.level<6?ue=1:_.level===6?ue=2:ue=3,de|=ue<<6,_.strstart!==0&&(de|=Te),de+=31-de%31,_.status=xe,nt(_,de),_.strstart!==0&&(nt(_,l.adler>>>16),nt(_,l.adler&65535)),l.adler=1}if(_.status===Fe)if(_.gzhead.extra){for(B=_.pending;_.gzindex<(_.gzhead.extra.length&65535)&&!(_.pending===_.pending_buf_size&&(_.gzhead.hcrc&&_.pending>B&&(l.adler=u(l.adler,_.pending_buf,_.pending-B,B)),Ee(l),B=_.pending,_.pending===_.pending_buf_size));)_e(_,_.gzhead.extra[_.gzindex]&255),_.gzindex++;_.gzhead.hcrc&&_.pending>B&&(l.adler=u(l.adler,_.pending_buf,_.pending-B,B)),_.gzindex===_.gzhead.extra.length&&(_.gzindex=0,_.status=be)}else _.status=be;if(_.status===be)if(_.gzhead.name){B=_.pending;do{if(_.pending===_.pending_buf_size&&(_.gzhead.hcrc&&_.pending>B&&(l.adler=u(l.adler,_.pending_buf,_.pending-B,B)),Ee(l),B=_.pending,_.pending===_.pending_buf_size)){V=1;break}_.gzindex<_.gzhead.name.length?V=_.gzhead.name.charCodeAt(_.gzindex++)&255:V=0,_e(_,V)}while(V!==0);_.gzhead.hcrc&&_.pending>B&&(l.adler=u(l.adler,_.pending_buf,_.pending-B,B)),V===0&&(_.gzindex=0,_.status=Pe)}else _.status=Pe;if(_.status===Pe)if(_.gzhead.comment){B=_.pending;do{if(_.pending===_.pending_buf_size&&(_.gzhead.hcrc&&_.pending>B&&(l.adler=u(l.adler,_.pending_buf,_.pending-B,B)),Ee(l),B=_.pending,_.pending===_.pending_buf_size)){V=1;break}_.gzindex<_.gzhead.comment.length?V=_.gzhead.comment.charCodeAt(_.gzindex++)&255:V=0,_e(_,V)}while(V!==0);_.gzhead.hcrc&&_.pending>B&&(l.adler=u(l.adler,_.pending_buf,_.pending-B,B)),V===0&&(_.status=Ue)}else _.status=Ue;if(_.status===Ue&&(_.gzhead.hcrc?(_.pending+2>_.pending_buf_size&&Ee(l),_.pending+2<=_.pending_buf_size&&(_e(_,l.adler&255),_e(_,l.adler>>8&255),l.adler=0,_.status=xe)):_.status=xe),_.pending!==0){if(Ee(l),l.avail_out===0)return _.last_flush=-1,E}else if(l.avail_in===0&&tt(M)<=tt(H)&&M!==m)return Ge(l,D);if(_.status===ke&&l.avail_in!==0)return Ge(l,D);if(l.avail_in!==0||_.lookahead!==0||M!==d&&_.status!==ke){var le=_.strategy===S?ht(_,M):_.strategy===R?ct(_,M):ot[_.level].func(_,M);if((le===He||le===ze)&&(_.status=ke),le===ve||le===He)return l.avail_out===0&&(_.last_flush=-1),E;if(le===Ie&&(M===c?s._tr_align(_):M!==N&&(s._tr_stored_block(_,0,0,!1),M===w&&(Je(_.head),_.lookahead===0&&(_.strstart=0,_.block_start=0,_.insert=0))),Ee(l),l.avail_out===0))return _.last_flush=-1,E}return M!==m?E:_.wrap<=0?f:(_.wrap===2?(_e(_,l.adler&255),_e(_,l.adler>>8&255),_e(_,l.adler>>16&255),_e(_,l.adler>>24&255),_e(_,l.total_in&255),_e(_,l.total_in>>8&255),_e(_,l.total_in>>16&255),_e(_,l.total_in>>24&255)):(nt(_,l.adler>>>16),nt(_,l.adler&65535)),Ee(l),_.wrap>0&&(_.wrap=-_.wrap),_.pending!==0?E:f)}function x(l){var M;return!l||!l.state?v:(M=l.state.status,M!==We&&M!==Fe&&M!==be&&M!==Pe&&M!==Ue&&M!==xe&&M!==ke?Ge(l,v):(l.state=null,M===xe?Ge(l,g):E))}function z(l,M){var H=M.length,_,B,V,de,ue,le,Ae,Xe;if(!l||!l.state||(_=l.state,de=_.wrap,de===2||de===1&&_.status!==We||_.lookahead))return v;for(de===1&&(l.adler=a(l.adler,M,H,0)),_.wrap=0,H>=_.w_size&&(de===0&&(Je(_.head),_.strstart=0,_.block_start=0,_.insert=0),Xe=new n.Buf8(_.w_size),n.arraySet(Xe,M,H-_.w_size,_.w_size,0),M=Xe,H=_.w_size),ue=l.avail_in,le=l.next_in,Ae=l.input,l.avail_in=H,l.next_in=0,l.input=M,it(_);_.lookahead>=ce;){B=_.strstart,V=_.lookahead-(ce-1);do _.ins_h=(_.ins_h<<_.hash_shift^_.window[B+ce-1])&_.hash_mask,_.prev[B&_.w_mask]=_.head[_.ins_h],_.head[_.ins_h]=B,B++;while(--V);_.strstart=B,_.lookahead=ce-1,it(_)}return _.strstart+=_.lookahead,_.block_start=_.strstart,_.insert=_.lookahead,_.lookahead=0,_.match_length=_.prev_length=ce-1,_.match_available=0,l.next_in=le,l.input=Ae,l.avail_in=ue,_.wrap=de,E}r.deflateInit=p,r.deflateInit2=F,r.deflateReset=te,r.deflateResetKeep=Q,r.deflateSetHeader=fe,r.deflate=o,r.deflateEnd=x,r.deflateSetDictionary=z,r.deflateInfo="pako deflate (from Nodeca project)"},{"../utils/common":41,"./adler32":43,"./crc32":45,"./messages":51,"./trees":52}],47:[function(t,i,r){function n(){this.text=0,this.time=0,this.xflags=0,this.os=0,this.extra=null,this.extra_len=0,this.name="",this.comment="",this.hcrc=0,this.done=!1}i.exports=n},{}],48:[function(t,i,r){var n=30,s=12;i.exports=function(u,h){var d,c,w,m,N,E,f,v,g,D,L,b,S,R,I,k,O,G,j,Z,Y,X,ie,K,$;d=u.state,c=u.next_in,K=u.input,w=c+(u.avail_in-5),m=u.next_out,$=u.output,N=m-(h-u.avail_out),E=m+(u.avail_out-257),f=d.dmax,v=d.wsize,g=d.whave,D=d.wnext,L=d.window,b=d.hold,S=d.bits,R=d.lencode,I=d.distcode,k=(1<>>24,b>>>=j,S-=j,j=G>>>16&255,j===0)$[m++]=G&65535;else if(j&16){Z=G&65535,j&=15,j&&(S>>=j,S-=j),S<15&&(b+=K[c++]<>>24,b>>>=j,S-=j,j=G>>>16&255,j&16){if(Y=G&65535,j&=15,Sf){u.msg="invalid distance too far back",d.mode=n;break e}if(b>>>=j,S-=j,j=m-N,Y>j){if(j=Y-j,j>g&&d.sane){u.msg="invalid distance too far back",d.mode=n;break e}if(X=0,ie=L,D===0){if(X+=v-j,j2;)$[m++]=ie[X++],$[m++]=ie[X++],$[m++]=ie[X++],Z-=3;Z&&($[m++]=ie[X++],Z>1&&($[m++]=ie[X++]))}else{X=m-Y;do $[m++]=$[X++],$[m++]=$[X++],$[m++]=$[X++],Z-=3;while(Z>2);Z&&($[m++]=$[X++],Z>1&&($[m++]=$[X++]))}}else if((j&64)===0){G=I[(G&65535)+(b&(1<>3,c-=Z,S-=Z<<3,b&=(1<>>24&255)+(F>>>8&65280)+((F&65280)<<8)+((F&255)<<24)}function pt(){this.mode=0,this.last=!1,this.wrap=0,this.havedict=!1,this.flags=0,this.dmax=0,this.check=0,this.total=0,this.head=null,this.wbits=0,this.wsize=0,this.whave=0,this.wnext=0,this.window=null,this.hold=0,this.bits=0,this.length=0,this.offset=0,this.extra=0,this.lencode=null,this.distcode=null,this.lenbits=0,this.distbits=0,this.ncode=0,this.nlen=0,this.ndist=0,this.have=0,this.next=null,this.lens=new n.Buf16(320),this.work=new n.Buf16(288),this.lendyn=null,this.distdyn=null,this.sane=0,this.back=0,this.was=0}function lt(F){var p;return!F||!F.state?D:(p=F.state,F.total_in=F.total_out=p.total=0,F.msg="",p.wrap&&(F.adler=p.wrap&1),p.mode=I,p.last=0,p.havedict=0,p.dmax=32768,p.head=null,p.hold=0,p.bits=0,p.lencode=p.lendyn=new n.Buf32(Je),p.distcode=p.distdyn=new n.Buf32(Ee),p.sane=1,p.back=-1,f)}function it(F){var p;return!F||!F.state?D:(p=F.state,p.wsize=0,p.whave=0,p.wnext=0,lt(F))}function vt(F,p){var o,x;return!F||!F.state||(x=F.state,p<0?(o=0,p=-p):(o=(p>>4)+1,p<48&&(p&=15)),p&&(p<8||p>15))?D:(x.window!==null&&x.wbits!==p&&(x.window=null),x.wrap=o,x.wbits=p,it(F))}function ft(F,p){var o,x;return F?(x=new pt,F.state=x,x.window=null,o=vt(F,p),o!==f&&(F.state=null),o):D}function at(F){return ft(F,_e)}var ct=!0,ht,Ye;function ot(F){if(ct){var p;for(ht=new n.Buf32(512),Ye=new n.Buf32(32),p=0;p<144;)F.lens[p++]=8;for(;p<256;)F.lens[p++]=9;for(;p<280;)F.lens[p++]=7;for(;p<288;)F.lens[p++]=8;for(h(c,F.lens,0,288,ht,0,F.work,{bits:9}),p=0;p<32;)F.lens[p++]=5;h(w,F.lens,0,32,Ye,0,F.work,{bits:5}),ct=!1}F.lencode=ht,F.lenbits=9,F.distcode=Ye,F.distbits=5}function wt(F,p,o,x){var z,l=F.state;return l.window===null&&(l.wsize=1<=l.wsize?(n.arraySet(l.window,p,o-l.wsize,l.wsize,0),l.wnext=0,l.whave=l.wsize):(z=l.wsize-l.wnext,z>x&&(z=x),n.arraySet(l.window,p,o-x,z,l.wnext),x-=z,x?(n.arraySet(l.window,p,o-x,x,0),l.wnext=x,l.whave=l.wsize):(l.wnext+=z,l.wnext===l.wsize&&(l.wnext=0),l.whave>>8&255,o.check=a(o.check,st,2,0),B=0,V=0,o.mode=k;break}if(o.flags=0,o.head&&(o.head.done=!1),!(o.wrap&1)||(((B&255)<<8)+(B>>8))%31){F.msg="incorrect header check",o.mode=Oe;break}if((B&15)!==R){F.msg="unknown compression method",o.mode=Oe;break}if(B>>>=4,V-=4,$e=(B&15)+8,o.wbits===0)o.wbits=$e;else if($e>o.wbits){F.msg="invalid window size",o.mode=Oe;break}o.dmax=1<<$e,F.adler=o.check=1,o.mode=B&512?K:ae,B=0,V=0;break;case k:for(;V<16;){if(H===0)break e;H--,B+=x[l++]<>8&1),o.flags&512&&(st[0]=B&255,st[1]=B>>>8&255,o.check=a(o.check,st,2,0)),B=0,V=0,o.mode=O;case O:for(;V<32;){if(H===0)break e;H--,B+=x[l++]<>>8&255,st[2]=B>>>16&255,st[3]=B>>>24&255,o.check=a(o.check,st,4,0)),B=0,V=0,o.mode=G;case G:for(;V<16;){if(H===0)break e;H--,B+=x[l++]<>8),o.flags&512&&(st[0]=B&255,st[1]=B>>>8&255,o.check=a(o.check,st,2,0)),B=0,V=0,o.mode=j;case j:if(o.flags&1024){for(;V<16;){if(H===0)break e;H--,B+=x[l++]<>>8&255,o.check=a(o.check,st,2,0)),B=0,V=0}else o.head&&(o.head.extra=null);o.mode=Z;case Z:if(o.flags&1024&&(le=o.length,le>H&&(le=H),le&&(o.head&&($e=o.head.extra_len-o.length,o.head.extra||(o.head.extra=new Array(o.head.extra_len)),n.arraySet(o.head.extra,x,l,le,$e)),o.flags&512&&(o.check=a(o.check,x,le,l)),H-=le,l+=le,o.length-=le),o.length))break e;o.length=0,o.mode=Y;case Y:if(o.flags&2048){if(H===0)break e;le=0;do $e=x[l+le++],o.head&&$e&&o.length<65536&&(o.head.name+=String.fromCharCode($e));while($e&&le>9&1,o.head.done=!0),F.adler=o.check=0,o.mode=ae;break;case K:for(;V<32;){if(H===0)break e;H--,B+=x[l++]<>>=V&7,V-=V&7,o.mode=Ie;break}for(;V<3;){if(H===0)break e;H--,B+=x[l++]<>>=1,V-=1,B&3){case 0:o.mode=ne;break;case 1:if(ot(o),o.mode=Fe,p===E){B>>>=2,V-=2;break e}break;case 2:o.mode=ye;break;case 3:F.msg="invalid block type",o.mode=Oe}B>>>=2,V-=2;break;case ne:for(B>>>=V&7,V-=V&7;V<32;){if(H===0)break e;H--,B+=x[l++]<>>16^65535)){F.msg="invalid stored block lengths",o.mode=Oe;break}if(o.length=B&65535,B=0,V=0,o.mode=ce,p===E)break e;case ce:o.mode=Ce;case Ce:if(le=o.length,le){if(le>H&&(le=H),le>_&&(le=_),le===0)break e;n.arraySet(z,x,l,le,M),H-=le,l+=le,_-=le,M+=le,o.length-=le;break}o.mode=ae;break;case ye:for(;V<14;){if(H===0)break e;H--,B+=x[l++]<>>=5,V-=5,o.ndist=(B&31)+1,B>>>=5,V-=5,o.ncode=(B&15)+4,B>>>=4,V-=4,o.nlen>286||o.ndist>30){F.msg="too many length or distance symbols",o.mode=Oe;break}o.have=0,o.mode=Te;case Te:for(;o.have>>=3,V-=3}for(;o.have<19;)o.lens[ii[o.have++]]=0;if(o.lencode=o.lendyn,o.lenbits=7,_t={bits:o.lenbits},dt=h(d,o.lens,0,19,o.lencode,0,o.work,_t),o.lenbits=_t.bits,dt){F.msg="invalid code lengths set",o.mode=Oe;break}o.have=0,o.mode=We;case We:for(;o.have>>24,Ne=Me>>>16&255,Qe=Me&65535,!(Se<=V);){if(H===0)break e;H--,B+=x[l++]<>>=Se,V-=Se,o.lens[o.have++]=Qe;else{if(Qe===16){for(gt=Se+2;V>>=Se,V-=Se,o.have===0){F.msg="invalid bit length repeat",o.mode=Oe;break}$e=o.lens[o.have-1],le=3+(B&3),B>>>=2,V-=2}else if(Qe===17){for(gt=Se+3;V>>=Se,V-=Se,$e=0,le=3+(B&7),B>>>=3,V-=3}else{for(gt=Se+7;V>>=Se,V-=Se,$e=0,le=11+(B&127),B>>>=7,V-=7}if(o.have+le>o.nlen+o.ndist){F.msg="invalid bit length repeat",o.mode=Oe;break}for(;le--;)o.lens[o.have++]=$e}}if(o.mode===Oe)break;if(o.lens[256]===0){F.msg="invalid code -- missing end-of-block",o.mode=Oe;break}if(o.lenbits=9,_t={bits:o.lenbits},dt=h(c,o.lens,0,o.nlen,o.lencode,0,o.work,_t),o.lenbits=_t.bits,dt){F.msg="invalid literal/lengths set",o.mode=Oe;break}if(o.distbits=6,o.distcode=o.distdyn,_t={bits:o.distbits},dt=h(w,o.lens,o.nlen,o.ndist,o.distcode,0,o.work,_t),o.distbits=_t.bits,dt){F.msg="invalid distances set",o.mode=Oe;break}if(o.mode=Fe,p===E)break e;case Fe:o.mode=be;case be:if(H>=6&&_>=258){F.next_out=M,F.avail_out=_,F.next_in=l,F.avail_in=H,o.hold=B,o.bits=V,u(F,ue),M=F.next_out,z=F.output,_=F.avail_out,l=F.next_in,x=F.input,H=F.avail_in,B=o.hold,V=o.bits,o.mode===ae&&(o.back=-1);break}for(o.back=0;Me=o.lencode[B&(1<>>24,Ne=Me>>>16&255,Qe=Me&65535,!(Se<=V);){if(H===0)break e;H--,B+=x[l++]<>Ze)],Se=Me>>>24,Ne=Me>>>16&255,Qe=Me&65535,!(Ze+Se<=V);){if(H===0)break e;H--,B+=x[l++]<>>=Ze,V-=Ze,o.back+=Ze}if(B>>>=Se,V-=Se,o.back+=Se,o.length=Qe,Ne===0){o.mode=ve;break}if(Ne&32){o.back=-1,o.mode=ae;break}if(Ne&64){F.msg="invalid literal/length code",o.mode=Oe;break}o.extra=Ne&15,o.mode=Pe;case Pe:if(o.extra){for(gt=o.extra;V>>=o.extra,V-=o.extra,o.back+=o.extra}o.was=o.length,o.mode=Ue;case Ue:for(;Me=o.distcode[B&(1<>>24,Ne=Me>>>16&255,Qe=Me&65535,!(Se<=V);){if(H===0)break e;H--,B+=x[l++]<>Ze)],Se=Me>>>24,Ne=Me>>>16&255,Qe=Me&65535,!(Ze+Se<=V);){if(H===0)break e;H--,B+=x[l++]<>>=Ze,V-=Ze,o.back+=Ze}if(B>>>=Se,V-=Se,o.back+=Se,Ne&64){F.msg="invalid distance code",o.mode=Oe;break}o.offset=Qe,o.extra=Ne&15,o.mode=xe;case xe:if(o.extra){for(gt=o.extra;V>>=o.extra,V-=o.extra,o.back+=o.extra}if(o.offset>o.dmax){F.msg="invalid distance too far back",o.mode=Oe;break}o.mode=ke;case ke:if(_===0)break e;if(le=ue-_,o.offset>le){if(le=o.offset-le,le>o.whave&&o.sane){F.msg="invalid distance too far back",o.mode=Oe;break}le>o.wnext?(le-=o.wnext,Ae=o.wsize-le):Ae=o.wnext-le,le>o.length&&(le=o.length),Xe=o.window}else Xe=z,Ae=M-o.offset,le=o.length;le>_&&(le=_),_-=le,o.length-=le;do z[M++]=Xe[Ae++];while(--le);o.length===0&&(o.mode=be);break;case ve:if(_===0)break e;z[M++]=o.length,_--,o.mode=be;break;case Ie:if(o.wrap){for(;V<32;){if(H===0)break e;H--,B|=x[l++]<=1&&be[Z]===0;Z--);if(Y>Z&&(Y=Z),Z===0)return b[S++]=1<<24|64<<16|0,b[S++]=1<<24|64<<16|0,I.bits=1,0;for(j=1;j0&&(v===h||Z!==1))return-1;for(Pe[1]=0,O=1;Oa||v===c&&$>u)return 1;for(;;){ke=O-ie,R[G]Fe?(ve=Ue[xe+R[G]],Ie=Te[We+R[G]]):(ve=96,Ie=0),pe=1<>ie)+ne]=ke<<24|ve<<16|Ie|0;while(ne!==0);for(pe=1<>=1;if(pe!==0?(ae&=pe-1,ae+=pe):ae=0,G++,--be[O]===0){if(O===Z)break;O=g[D+R[G]]}if(O>Y&&(ae&Ce)!==ce){for(ie===0&&(ie=Y),ye+=j,X=O-ie,K=1<a||v===c&&$>u)return 1;ce=ae&Ce,b[ce]=Y<<24|X<<16|ye-S|0}}return ae!==0&&(b[ye+ae]=O-ie<<24|64<<16|0),I.bits=Y,0}},{"../utils/common":41}],51:[function(t,i,r){i.exports={2:"need dictionary",1:"stream end",0:"","-1":"file error","-2":"stream error","-3":"data error","-4":"insufficient memory","-5":"buffer error","-6":"incompatible version"}},{}],52:[function(t,i,r){var n=t("../utils/common"),s=4,a=0,u=1,h=2;function d(C){for(var Q=C.length;--Q>=0;)C[Q]=0}var c=0,w=1,m=2,N=3,E=258,f=29,v=256,g=v+1+f,D=30,L=19,b=2*g+1,S=15,R=16,I=7,k=256,O=16,G=17,j=18,Z=[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0],Y=[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13],X=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7],ie=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],K=512,$=new Array((g+2)*2);d($);var ae=new Array(D*2);d(ae);var pe=new Array(K);d(pe);var ne=new Array(E-N+1);d(ne);var ce=new Array(f);d(ce);var Ce=new Array(D);d(Ce);function ye(C,Q,te,fe,F){this.static_tree=C,this.extra_bits=Q,this.extra_base=te,this.elems=fe,this.max_length=F,this.has_stree=C&&C.length}var Te,We,Fe;function be(C,Q){this.dyn_tree=C,this.max_code=0,this.stat_desc=Q}function Pe(C){return C<256?pe[C]:pe[256+(C>>>7)]}function Ue(C,Q){C.pending_buf[C.pending++]=Q&255,C.pending_buf[C.pending++]=Q>>>8&255}function xe(C,Q,te){C.bi_valid>R-te?(C.bi_buf|=Q<>R-C.bi_valid,C.bi_valid+=te-R):(C.bi_buf|=Q<>>=1,te<<=1;while(--Q>0);return te>>>1}function Ie(C){C.bi_valid===16?(Ue(C,C.bi_buf),C.bi_buf=0,C.bi_valid=0):C.bi_valid>=8&&(C.pending_buf[C.pending++]=C.bi_buf&255,C.bi_buf>>=8,C.bi_valid-=8)}function He(C,Q){var te=Q.dyn_tree,fe=Q.max_code,F=Q.stat_desc.static_tree,p=Q.stat_desc.has_stree,o=Q.stat_desc.extra_bits,x=Q.stat_desc.extra_base,z=Q.stat_desc.max_length,l,M,H,_,B,V,de=0;for(_=0;_<=S;_++)C.bl_count[_]=0;for(te[C.heap[C.heap_max]*2+1]=0,l=C.heap_max+1;lz&&(_=z,de++),te[M*2+1]=_,!(M>fe)&&(C.bl_count[_]++,B=0,M>=x&&(B=o[M-x]),V=te[M*2],C.opt_len+=V*(_+B),p&&(C.static_len+=V*(F[M*2+1]+B)));if(de!==0){do{for(_=z-1;C.bl_count[_]===0;)_--;C.bl_count[_]--,C.bl_count[_+1]+=2,C.bl_count[z]--,de-=2}while(de>0);for(_=z;_!==0;_--)for(M=C.bl_count[_];M!==0;)H=C.heap[--l],!(H>fe)&&(te[H*2+1]!==_&&(C.opt_len+=(_-te[H*2+1])*te[H*2],te[H*2+1]=_),M--)}}function ze(C,Q,te){var fe=new Array(S+1),F=0,p,o;for(p=1;p<=S;p++)fe[p]=F=F+te[p-1]<<1;for(o=0;o<=Q;o++){var x=C[o*2+1];x!==0&&(C[o*2]=ve(fe[x]++,x))}}function Oe(){var C,Q,te,fe,F,p=new Array(S+1);for(te=0,fe=0;fe>=7;fe8?Ue(C,C.bi_buf):C.bi_valid>0&&(C.pending_buf[C.pending++]=C.bi_buf),C.bi_buf=0,C.bi_valid=0}function Je(C,Q,te,fe){tt(C),Ue(C,te),Ue(C,~te),n.arraySet(C.pending_buf,C.window,Q,te,C.pending),C.pending+=te}function Ee(C,Q,te,fe){var F=Q*2,p=te*2;return C[F]>1;o>=1;o--)De(C,te,o);l=p;do o=C.heap[1],C.heap[1]=C.heap[C.heap_len--],De(C,te,1),x=C.heap[1],C.heap[--C.heap_max]=o,C.heap[--C.heap_max]=x,te[l*2]=te[o*2]+te[x*2],C.depth[l]=(C.depth[o]>=C.depth[x]?C.depth[o]:C.depth[x])+1,te[o*2+1]=te[x*2+1]=l,C.heap[1]=l++,De(C,te,1);while(C.heap_len>=2);C.heap[--C.heap_max]=C.heap[1],He(C,Q),ze(te,z,C.bl_count)}function pt(C,Q,te){var fe,F=-1,p,o=Q[1],x=0,z=7,l=4;for(o===0&&(z=138,l=3),Q[(te+1)*2+1]=65535,fe=0;fe<=te;fe++)p=o,o=Q[(fe+1)*2+1],!(++x=3&&C.bl_tree[ie[Q]*2+1]===0;Q--);return C.opt_len+=3*(Q+1)+5+5+4,Q}function vt(C,Q,te,fe){var F;for(xe(C,Q-257,5),xe(C,te-1,5),xe(C,fe-4,4),F=0;F>>=1)if(Q&1&&C.dyn_ltree[te*2]!==0)return a;if(C.dyn_ltree[18]!==0||C.dyn_ltree[20]!==0||C.dyn_ltree[26]!==0)return u;for(te=32;te0?(C.strm.data_type===h&&(C.strm.data_type=ft(C)),nt(C,C.l_desc),nt(C,C.d_desc),o=it(C),F=C.opt_len+3+7>>>3,p=C.static_len+3+7>>>3,p<=F&&(F=p)):F=p=te+5,te+4<=F&&Q!==-1?ht(C,Q,te,fe):C.strategy===s||p===F?(xe(C,(w<<1)+(fe?1:0),3),_e(C,$,ae)):(xe(C,(m<<1)+(fe?1:0),3),vt(C,C.l_desc.max_code+1,C.d_desc.max_code+1,o+1),_e(C,C.dyn_ltree,C.dyn_dtree)),Ge(C),fe&&tt(C)}function wt(C,Q,te){return C.pending_buf[C.d_buf+C.last_lit*2]=Q>>>8&255,C.pending_buf[C.d_buf+C.last_lit*2+1]=Q&255,C.pending_buf[C.l_buf+C.last_lit]=te&255,C.last_lit++,Q===0?C.dyn_ltree[te*2]++:(C.matches++,Q--,C.dyn_ltree[(ne[te]+v+1)*2]++,C.dyn_dtree[Pe(Q)*2]++),C.last_lit===C.lit_bufsize-1}r._tr_init=ct,r._tr_stored_block=ht,r._tr_flush_block=ot,r._tr_tally=wt,r._tr_align=Ye},{"../utils/common":41}],53:[function(t,i,r){function n(){this.input=null,this.next_in=0,this.avail_in=0,this.total_in=0,this.output=null,this.next_out=0,this.avail_out=0,this.total_out=0,this.msg="",this.state=null,this.data_type=2,this.adler=0}i.exports=n},{}],54:[function(t,i,r){(function(n){(function(s,a){if(s.setImmediate)return;var u=1,h={},d=!1,c=s.document,w;function m(I){typeof I!="function"&&(I=new Function(""+I));for(var k=new Array(arguments.length-1),O=0;O"u"?typeof n>"u"?this:n:self)}).call(this,typeof bt<"u"?bt:typeof self<"u"?self:typeof window<"u"?window:{})},{}]},{},[10])(10)})})(fr)),fr.exports}var Xa=Ya();const $a=Yt(Xa);class Ka{constructor(){this.zip=void 0,this.urlCache={},this.checkRequirements()}checkRequirements(){try{this.zip=new $a}catch{throw new Error("JSZip lib not loaded")}}open(e,t){return this.zip.loadAsync(e,{base64:t})}openUrl(e,t){return ti(e,"binary").then((function(i){return this.zip.loadAsync(i,{base64:t})}).bind(this))}request(e,t){var i=new Le,r,n=new It(e);return t||(t=n.extension),t=="blob"?r=this.getBlob(e):r=this.getText(e),r?r.then((function(s){let a=this.handleResponse(s,t);i.resolve(a)}).bind(this)):i.reject({message:"File not found in the epub: "+e,stack:new Error().stack}),i.promise}handleResponse(e,t){var i;return t=="json"?i=JSON.parse(e):ei(t)?i=Ct(e,"text/xml"):t=="xhtml"?i=Ct(e,"application/xhtml+xml"):t=="html"||t=="htm"?i=Ct(e,"text/html"):i=e,i}getBlob(e,t){var i=window.decodeURIComponent(e.substr(1)),r=this.zip.file(i);if(r)return t=t||Gt.lookup(r.name),r.async("uint8array").then(function(n){return new Blob([n],{type:t})})}getText(e,t){var i=window.decodeURIComponent(e.substr(1)),r=this.zip.file(i);if(r)return r.async("string").then(function(n){return n})}getBase64(e,t){var i=window.decodeURIComponent(e.substr(1)),r=this.zip.file(i);if(r)return t=t||Gt.lookup(r.name),r.async("base64").then(function(n){return"data:"+t+";base64,"+n})}createUrl(e,t){var i=new Le,r=window.URL||window.webkitURL||window.mozURL,n,s,a=t&&t.base64;return e in this.urlCache?(i.resolve(this.urlCache[e]),i.promise):(a?(s=this.getBase64(e),s&&s.then((function(u){this.urlCache[e]=u,i.resolve(u)}).bind(this))):(s=this.getBlob(e),s&&s.then((function(u){n=r.createObjectURL(u),this.urlCache[e]=n,i.resolve(n)}).bind(this))),s||i.reject({message:"File not found in the epub: "+e,stack:new Error().stack}),i.promise)}revokeUrl(e){var t=window.URL||window.webkitURL||window.mozURL,i=this.urlCache[e];i&&t.revokeObjectURL(i)}destroy(){var e=window.URL||window.webkitURL||window.mozURL;for(let t in this.urlCache)e.revokeObjectURL(t);this.zip=void 0,this.urlCache={}}}var cr={exports:{}};/*! + localForage -- Offline Storage, Improved + Version 1.10.0 + https://localforage.github.io/localForage + (c) 2013-2017 Mozilla, Apache License 2.0 +*/var Rn;function Ja(){return Rn||(Rn=1,(function(y,e){(function(t){y.exports=t()})(function(){return(function t(i,r,n){function s(h,d){if(!r[h]){if(!i[h]){var c=typeof Lt=="function"&≪if(!d&&c)return c(h,!0);if(a)return a(h,!0);var w=new Error("Cannot find module '"+h+"'");throw w.code="MODULE_NOT_FOUND",w}var m=r[h]={exports:{}};i[h][0].call(m.exports,function(N){var E=i[h][1][N];return s(E||N)},m,m.exports,t,i,r,n)}return r[h].exports}for(var a=typeof Lt=="function"&&Lt,u=0;u"u"&&t(3);var c=Promise;function w(A,P){P&&A.then(function(T){P(null,T)},function(T){P(T)})}function m(A,P,T){typeof P=="function"&&A.then(P),typeof T=="function"&&A.catch(T)}function N(A){return typeof A!="string"&&(console.warn(A+" used as a key, but it is not a string."),A=String(A)),A}function E(){if(arguments.length&&typeof arguments[arguments.length-1]=="function")return arguments[arguments.length-1]}var f="local-forage-detect-blob-support",v=void 0,g={},D=Object.prototype.toString,L="readonly",b="readwrite";function S(A){for(var P=A.length,T=new ArrayBuffer(P),q=new Uint8Array(T),W=0;W=43)}}).catch(function(){return!1})}function I(A){return typeof v=="boolean"?c.resolve(v):R(A).then(function(P){return v=P,v})}function k(A){var P=g[A.name],T={};T.promise=new c(function(q,W){T.resolve=q,T.reject=W}),P.deferredOperations.push(T),P.dbReady?P.dbReady=P.dbReady.then(function(){return T.promise}):P.dbReady=T.promise}function O(A){var P=g[A.name],T=P.deferredOperations.pop();if(T)return T.resolve(),T.promise}function G(A,P){var T=g[A.name],q=T.deferredOperations.pop();if(q)return q.reject(P),q.promise}function j(A,P){return new c(function(T,q){if(g[A.name]=g[A.name]||ce(),A.db)if(P)k(A),A.db.close();else return T(A.db);var W=[A.name];P&&W.push(A.version);var U=u.open.apply(u,W);P&&(U.onupgradeneeded=function(J){var ee=U.result;try{ee.createObjectStore(A.storeName),J.oldVersion<=1&&ee.createObjectStore(f)}catch(re){if(re.name==="ConstraintError")console.warn('The database "'+A.name+'" has been upgraded from version '+J.oldVersion+" to version "+J.newVersion+', but the storage "'+A.storeName+'" already exists.');else throw re}}),U.onerror=function(J){J.preventDefault(),q(U.error)},U.onsuccess=function(){var J=U.result;J.onversionchange=function(ee){ee.target.close()},T(J),O(A)}})}function Z(A){return j(A,!1)}function Y(A){return j(A,!0)}function X(A,P){if(!A.db)return!0;var T=!A.db.objectStoreNames.contains(A.storeName),q=A.versionA.db.version;if(q&&(A.version!==P&&console.warn('The database "'+A.name+`" can't be downgraded from version `+A.db.version+" to version "+A.version+"."),A.version=A.db.version),W||T){if(T){var U=A.db.version+1;U>A.version&&(A.version=U)}return!0}return!1}function ie(A){return new c(function(P,T){var q=new FileReader;q.onerror=T,q.onloadend=function(W){var U=btoa(W.target.result||"");P({__local_forage_encoded_blob:!0,data:U,type:A.type})},q.readAsBinaryString(A)})}function K(A){var P=S(atob(A.data));return d([P],{type:A.type})}function $(A){return A&&A.__local_forage_encoded_blob}function ae(A){var P=this,T=P._initReady().then(function(){var q=g[P._dbInfo.name];if(q&&q.dbReady)return q.dbReady});return m(T,A,A),T}function pe(A){k(A);for(var P=g[A.name],T=P.forages,q=0;q0&&(!A.db||U.name==="InvalidStateError"||U.name==="NotFoundError"))return c.resolve().then(function(){if(!A.db||U.name==="NotFoundError"&&!A.db.objectStoreNames.contains(A.storeName)&&A.version<=A.db.version)return A.db&&(A.version=A.db.version+1),Y(A)}).then(function(){return pe(A).then(function(){ne(A,P,T,q-1)})}).catch(T);T(U)}}function ce(){return{forages:[],db:null,dbReady:null,deferredOperations:[]}}function Ce(A){var P=this,T={db:null};if(A)for(var q in A)T[q]=A[q];var W=g[T.name];W||(W=ce(),g[T.name]=W),W.forages.push(P),P._initReady||(P._initReady=P.ready,P.ready=ae);var U=[];function J(){return c.resolve()}for(var ee=0;ee>4,oe[W++]=(J&15)<<4|ee>>2,oe[W++]=(ee&3)<<6|re&63;return se}function ot(A){var P=new Uint8Array(A),T="",q;for(q=0;q>2],T+=He[(P[q]&3)<<4|P[q+1]>>4],T+=He[(P[q+1]&15)<<2|P[q+2]>>6],T+=He[P[q+2]&63];return P.length%3===2?T=T.substring(0,T.length-1)+"=":P.length%3===1&&(T=T.substring(0,T.length-2)+"=="),T}function wt(A,P){var T="";if(A&&(T=ht.call(A)),A&&(T==="[object ArrayBuffer]"||A.buffer&&ht.call(A.buffer)==="[object ArrayBuffer]")){var q,W=Ge;A instanceof ArrayBuffer?(q=A,W+=Je):(q=A.buffer,T==="[object Int8Array]"?W+=De:T==="[object Uint8Array]"?W+=_e:T==="[object Uint8ClampedArray]"?W+=nt:T==="[object Int16Array]"?W+=pt:T==="[object Uint16Array]"?W+=it:T==="[object Int32Array]"?W+=lt:T==="[object Uint32Array]"?W+=vt:T==="[object Float32Array]"?W+=ft:T==="[object Float64Array]"?W+=at:P(new Error("Failed to get type for BinaryArray"))),P(W+ot(q))}else if(T==="[object Blob]"){var U=new FileReader;U.onload=function(){var J=ze+A.type+"~"+ot(this.result);P(Ge+Ee+J)},U.readAsArrayBuffer(A)}else try{P(JSON.stringify(A))}catch(J){console.error("Couldn't convert value into a JSON string: ",A),P(null,J)}}function C(A){if(A.substring(0,tt)!==Ge)return JSON.parse(A);var P=A.substring(ct),T=A.substring(tt,ct),q;if(T===Ee&&Oe.test(P)){var W=P.match(Oe);q=W[1],P=P.substring(W[0].length)}var U=Ye(P);switch(T){case Je:return U;case Ee:return d([U],{type:q});case De:return new Int8Array(U);case _e:return new Uint8Array(U);case nt:return new Uint8ClampedArray(U);case pt:return new Int16Array(U);case it:return new Uint16Array(U);case lt:return new Int32Array(U);case vt:return new Uint32Array(U);case ft:return new Float32Array(U);case at:return new Float64Array(U);default:throw new Error("Unkown type: "+T)}}var Q={serialize:wt,deserialize:C,stringToBuffer:Ye,bufferToString:ot};function te(A,P,T,q){A.executeSql("CREATE TABLE IF NOT EXISTS "+P.storeName+" (id INTEGER PRIMARY KEY, key unique, value)",[],T,q)}function fe(A){var P=this,T={db:null};if(A)for(var q in A)T[q]=typeof A[q]!="string"?A[q].toString():A[q];var W=new c(function(U,J){try{T.db=openDatabase(T.name,String(T.version),T.description,T.size)}catch(ee){return J(ee)}T.db.transaction(function(ee){te(ee,T,function(){P._dbInfo=T,U()},function(re,se){J(se)})},J)});return T.serializer=Q,W}function F(A,P,T,q,W,U){A.executeSql(T,q,W,function(J,ee){ee.code===ee.SYNTAX_ERR?J.executeSql("SELECT name FROM sqlite_master WHERE type='table' AND name = ?",[P.storeName],function(re,se){se.rows.length?U(re,ee):te(re,P,function(){re.executeSql(T,q,W,U)},U)},U):U(J,ee)},U)}function p(A,P){var T=this;A=N(A);var q=new c(function(W,U){T.ready().then(function(){var J=T._dbInfo;J.db.transaction(function(ee){F(ee,J,"SELECT * FROM "+J.storeName+" WHERE key = ? LIMIT 1",[A],function(re,se){var oe=se.rows.length?se.rows.item(0).value:null;oe&&(oe=J.serializer.deserialize(oe)),W(oe)},function(re,se){U(se)})})}).catch(U)});return w(q,P),q}function o(A,P){var T=this,q=new c(function(W,U){T.ready().then(function(){var J=T._dbInfo;J.db.transaction(function(ee){F(ee,J,"SELECT * FROM "+J.storeName,[],function(re,se){for(var oe=se.rows,ge=oe.length,Re=0;Re0){J(x.apply(W,[A,re,T,q-1]));return}ee(Re)}})})}).catch(ee)});return w(U,T),U}function z(A,P,T){return x.apply(this,[A,P,T,1])}function l(A,P){var T=this;A=N(A);var q=new c(function(W,U){T.ready().then(function(){var J=T._dbInfo;J.db.transaction(function(ee){F(ee,J,"DELETE FROM "+J.storeName+" WHERE key = ?",[A],function(){W()},function(re,se){U(se)})})}).catch(U)});return w(q,P),q}function M(A){var P=this,T=new c(function(q,W){P.ready().then(function(){var U=P._dbInfo;U.db.transaction(function(J){F(J,U,"DELETE FROM "+U.storeName,[],function(){q()},function(ee,re){W(re)})})}).catch(W)});return w(T,A),T}function H(A){var P=this,T=new c(function(q,W){P.ready().then(function(){var U=P._dbInfo;U.db.transaction(function(J){F(J,U,"SELECT COUNT(key) as c FROM "+U.storeName,[],function(ee,re){var se=re.rows.item(0).c;q(se)},function(ee,re){W(re)})})}).catch(W)});return w(T,A),T}function _(A,P){var T=this,q=new c(function(W,U){T.ready().then(function(){var J=T._dbInfo;J.db.transaction(function(ee){F(ee,J,"SELECT key FROM "+J.storeName+" WHERE id = ? LIMIT 1",[A+1],function(re,se){var oe=se.rows.length?se.rows.item(0).key:null;W(oe)},function(re,se){U(se)})})}).catch(U)});return w(q,P),q}function B(A){var P=this,T=new c(function(q,W){P.ready().then(function(){var U=P._dbInfo;U.db.transaction(function(J){F(J,U,"SELECT key FROM "+U.storeName,[],function(ee,re){for(var se=[],oe=0;oe '__WebKitDatabaseInfoTable__'",[],function(W,U){for(var J=[],ee=0;ee0}function Se(A){var P=this,T={};if(A)for(var q in A)T[q]=A[q];return T.keyPrefix=Ae(A,P._defaultConfig),Me()?(P._dbInfo=T,T.serializer=Q,c.resolve()):c.reject()}function Ne(A){var P=this,T=P.ready().then(function(){for(var q=P._dbInfo.keyPrefix,W=localStorage.length-1;W>=0;W--){var U=localStorage.key(W);U.indexOf(q)===0&&localStorage.removeItem(U)}});return w(T,A),T}function Qe(A,P){var T=this;A=N(A);var q=T.ready().then(function(){var W=T._dbInfo,U=localStorage.getItem(W.keyPrefix+A);return U&&(U=W.serializer.deserialize(U)),U});return w(q,P),q}function Ze(A,P){var T=this,q=T.ready().then(function(){for(var W=T._dbInfo,U=W.keyPrefix,J=U.length,ee=localStorage.length,re=1,se=0;se=0;J--){var ee=localStorage.key(J);ee.indexOf(U)===0&&localStorage.removeItem(ee)}}):W=c.reject("Invalid arguments"),w(W,P),W}var gt={_driver:"localStorageWrapper",_initStorage:Se,_support:le(),iterate:Ze,getItem:Qe,setItem:st,removeItem:dt,clear:Ne,length:$e,key:Tt,keys:Pt,dropInstance:_t},ii=function(P,T){return P===T||typeof P=="number"&&typeof T=="number"&&isNaN(P)&&isNaN(T)},vs=function(P,T){for(var q=P.length,W=0;W"u"?"undefined":n(T))==="object"){if(this._ready)return new Error("Can't call config() after localforage has been used.");for(var q in T){if(q==="storeName"&&(T[q]=T[q].replace(/\W/g,"_")),q==="version"&&typeof T[q]!="number")return new Error("Database version must be a number.");this._config[q]=T[q]}return"driver"in T&&T.driver?this.setDriver(this._config.driver):!0}else return typeof T=="string"?this._config[T]:this._config},A.prototype.defineDriver=function(T,q,W){var U=new c(function(J,ee){try{var re=T._driver,se=new Error("Custom driver not compliant; see https://mozilla.github.io/localForage/#definedriver");if(!T._driver){ee(se);return}for(var oe=Ei.concat("_initStorage"),ge=0,Re=oe.length;ge"u"&&(e=In),this.storage=e.createInstance({name:this.name})}catch{throw new Error("localForage lib not loaded")}}addListeners(){this._status=this.status.bind(this),window.addEventListener("online",this._status),window.addEventListener("offline",this._status)}removeListeners(){window.removeEventListener("online",this._status),window.removeEventListener("offline",this._status),this._status=void 0}status(e){let t=navigator.onLine;this.online=t,t?this.emit("online",this):this.emit("offline",this)}add(e,t){let i=e.resources.map(r=>{let{href:n}=r,s=this.resolver(n),a=window.encodeURIComponent(s);return this.storage.getItem(a).then(u=>!u||t?this.requester(s,"binary").then(h=>this.storage.setItem(a,h)):u)});return Promise.all(i)}put(e,t,i){let r=window.encodeURIComponent(e);return this.storage.getItem(r).then(n=>n||this.requester(e,"binary",t,i).then(s=>this.storage.setItem(r,s)))}request(e,t,i,r){return this.online?this.requester(e,t,i,r).then(n=>(this.put(e),n)):this.retrieve(e,t)}retrieve(e,t){new Le;var i,r=new It(e);return t||(t=r.extension),t=="blob"?i=this.getBlob(e):i=this.getText(e),i.then(n=>{var s=new Le,a;return n?(a=this.handleResponse(n,t),s.resolve(a)):s.reject({message:"File not found in storage: "+e,stack:new Error().stack}),s.promise})}handleResponse(e,t){var i;return t=="json"?i=JSON.parse(e):ei(t)?i=Ct(e,"text/xml"):t=="xhtml"?i=Ct(e,"application/xhtml+xml"):t=="html"||t=="htm"?i=Ct(e,"text/html"):i=e,i}getBlob(e,t){let i=window.encodeURIComponent(e);return this.storage.getItem(i).then(function(r){if(r)return t=t||Gt.lookup(e),new Blob([r],{type:t})})}getText(e,t){let i=window.encodeURIComponent(e);return t=t||Gt.lookup(e),this.storage.getItem(i).then(function(r){var n=new Le,s=new FileReader,a;if(r)return a=new Blob([r],{type:t}),s.addEventListener("loadend",()=>{n.resolve(s.result)}),s.readAsText(a,t),n.promise})}getBase64(e,t){let i=window.encodeURIComponent(e);return t=t||Gt.lookup(e),this.storage.getItem(i).then(r=>{var n=new Le,s=new FileReader,a;if(r)return a=new Blob([r],{type:t}),s.addEventListener("loadend",()=>{n.resolve(s.result)}),s.readAsDataURL(a,t),n.promise})}createUrl(e,t){var i=new Le,r=window.URL||window.webkitURL||window.mozURL,n,s,a=t&&t.base64;return e in this.urlCache?(i.resolve(this.urlCache[e]),i.promise):(a?(s=this.getBase64(e),s&&s.then((function(u){this.urlCache[e]=u,i.resolve(u)}).bind(this))):(s=this.getBlob(e),s&&s.then((function(u){n=r.createObjectURL(u),this.urlCache[e]=n,i.resolve(n)}).bind(this))),s||i.reject({message:"File not found in storage: "+e,stack:new Error().stack}),i.promise)}revokeUrl(e){var t=window.URL||window.webkitURL||window.mozURL,i=this.urlCache[e];i&&t.revokeObjectURL(i)}destroy(){var e=window.URL||window.webkitURL||window.mozURL;for(let t in this.urlCache)e.revokeObjectURL(t);this.urlCache={},this.removeListeners()}}St(cs.prototype);class dr{constructor(e){this.interactive="",this.fixedLayout="",this.openToSpread="",this.orientationLock="",e&&this.parse(e)}parse(e){if(!e)return this;const t=Ve(e,"display_options");return t?(Rt(t,"option").forEach(r=>{let n="";switch(r.childNodes.length&&(n=r.childNodes[0].nodeValue),r.attributes.name.value){case"interactive":this.interactive=n;break;case"fixed-layout":this.fixedLayout=n;break;case"open-to-spread":this.openToSpread=n;break;case"orientation-lock":this.orientationLock=n;break}}),this):this}destroy(){this.interactive=void 0,this.fixedLayout=void 0,this.openToSpread=void 0,this.orientationLock=void 0}}const On="META-INF/container.xml",eo="META-INF/com.apple.ibooks.display-options.xml",xt={BINARY:"binary",BASE64:"base64",EPUB:"epub",OPF:"opf",MANIFEST:"json",DIRECTORY:"directory"};class Nr{constructor(e,t){typeof t>"u"&&typeof e!="string"&&!(e instanceof Blob)&&!(e instanceof ArrayBuffer)&&(t=e,e=void 0),this.settings=ut(this.settings||{},{requestMethod:void 0,requestCredentials:void 0,requestHeaders:void 0,encoding:void 0,replacements:void 0,canonical:void 0,openAs:void 0,store:void 0}),ut(this.settings,t),this.opening=new Le,this.opened=this.opening.promise,this.isOpen=!1,this.loading={manifest:new Le,spine:new Le,metadata:new Le,cover:new Le,navigation:new Le,pageList:new Le,resources:new Le,displayOptions:new Le},this.loaded={manifest:this.loading.manifest.promise,spine:this.loading.spine.promise,metadata:this.loading.metadata.promise,cover:this.loading.cover.promise,navigation:this.loading.navigation.promise,pageList:this.loading.pageList.promise,resources:this.loading.resources.promise,displayOptions:this.loading.displayOptions.promise},this.ready=Promise.all([this.loaded.manifest,this.loaded.spine,this.loaded.metadata,this.loaded.cover,this.loaded.navigation,this.loaded.resources,this.loaded.displayOptions]),this.isRendered=!1,this.request=this.settings.requestMethod||ti,this.spine=new pa,this.locations=new ns(this.spine,this.load.bind(this)),this.navigation=void 0,this.pageList=void 0,this.url=void 0,this.path=void 0,this.archived=!1,this.archive=void 0,this.storage=void 0,this.resources=void 0,this.rendition=void 0,this.container=void 0,this.packaging=void 0,this.displayOptions=void 0,this.settings.store&&this.store(this.settings.store),e&&this.open(e,this.settings.openAs).catch(i=>{var r=new Error("Cannot load book at "+e);this.emit(he.BOOK.OPEN_FAILED,r)})}open(e,t){var i,r=t||this.determineType(e);return r===xt.BINARY?(this.archived=!0,this.url=new At("/",""),i=this.openEpub(e)):r===xt.BASE64?(this.archived=!0,this.url=new At("/",""),i=this.openEpub(e,r)):r===xt.EPUB?(this.archived=!0,this.url=new At("/",""),i=this.request(e,"binary",this.settings.requestCredentials,this.settings.requestHeaders).then(this.openEpub.bind(this))):r==xt.OPF?(this.url=new At(e),i=this.openPackaging(this.url.Path.toString())):r==xt.MANIFEST?(this.url=new At(e),i=this.openManifest(this.url.Path.toString())):(this.url=new At(e),i=this.openContainer(On).then(this.openPackaging.bind(this))),i}openEpub(e,t){return this.unarchive(e,t||this.settings.encoding).then(()=>this.openContainer(On)).then(i=>this.openPackaging(i))}openContainer(e){return this.load(e).then(t=>(this.container=new va(t),this.resolve(this.container.packagePath)))}openPackaging(e){return this.path=new It(e),this.load(e).then(t=>(this.packaging=new hn(t),this.unpack(this.packaging)))}openManifest(e){return this.path=new It(e),this.load(e).then(t=>(this.packaging=new hn,this.packaging.load(t),this.unpack(this.packaging)))}load(e){var t=this.resolve(e);return this.archived?this.archive.request(t):this.request(t,null,this.settings.requestCredentials,this.settings.requestHeaders)}resolve(e,t){if(e){var i=e,r=e.indexOf("://")>-1;return r?e:(this.path&&(i=this.path.resolve(e)),t!=!1&&this.url&&(i=this.url.resolve(i)),i)}}canonical(e){var t=e;return e?(this.settings.canonical?t=this.settings.canonical(e):t=this.resolve(e,!0),t):""}determineType(e){var t,i,r;if(this.settings.encoding==="base64")return xt.BASE64;if(typeof e!="string")return xt.BINARY;if(t=new At(e),i=t.path(),r=i.extension,r&&(r=r.replace(/\?.*$/,"")),!r)return xt.DIRECTORY;if(r==="epub")return xt.EPUB;if(r==="opf")return xt.OPF;if(r==="json")return xt.MANIFEST}unpack(e){this.package=e,this.packaging.metadata.layout===""?this.load(this.url.resolve(eo)).then(t=>{this.displayOptions=new dr(t),this.loading.displayOptions.resolve(this.displayOptions)}).catch(t=>{this.displayOptions=new dr,this.loading.displayOptions.resolve(this.displayOptions)}):(this.displayOptions=new dr,this.loading.displayOptions.resolve(this.displayOptions)),this.spine.unpack(this.packaging,this.resolve.bind(this),this.canonical.bind(this)),this.resources=new ba(this.packaging.manifest,{archive:this.archive,resolver:this.resolve.bind(this),request:this.request.bind(this),replacements:this.settings.replacements||(this.archived?"blobUrl":"base64")}),this.loadNavigation(this.packaging).then(()=>{this.loading.navigation.resolve(this.navigation)}),this.packaging.coverPath&&(this.cover=this.resolve(this.packaging.coverPath)),this.loading.manifest.resolve(this.packaging.manifest),this.loading.metadata.resolve(this.packaging.metadata),this.loading.spine.resolve(this.spine),this.loading.cover.resolve(this.cover),this.loading.resources.resolve(this.resources),this.loading.pageList.resolve(this.pageList),this.isOpen=!0,this.archived||this.settings.replacements&&this.settings.replacements!="none"?this.replacements().then(()=>{this.loaded.displayOptions.then(()=>{this.opening.resolve(this)})}).catch(t=>{console.error(t)}):this.loaded.displayOptions.then(()=>{this.opening.resolve(this)})}loadNavigation(e){let t=e.navPath||e.ncxPath,i=e.toc;return i?new Promise((r,n)=>{this.navigation=new Yi(i),e.pageList&&(this.pageList=new Xi(e.pageList)),r(this.navigation)}):t?this.load(t,"xml").then(r=>(this.navigation=new Yi(r),this.pageList=new Xi(r),this.navigation)):new Promise((r,n)=>{this.navigation=new Yi,this.pageList=new Xi,r(this.navigation)})}section(e){return this.spine.get(e)}renderTo(e,t){return this.rendition=new kr(this,t),this.rendition.attachTo(e),this.rendition}setRequestCredentials(e){this.settings.requestCredentials=e}setRequestHeaders(e){this.settings.requestHeaders=e}unarchive(e,t){return this.archive=new Ka,this.archive.open(e,t)}store(e){let t=this.settings.replacements&&this.settings.replacements!=="none",i=this.url,r=this.settings.requestMethod||ti.bind(this);return this.storage=new cs(e,r,this.resolve.bind(this)),this.request=this.storage.request.bind(this.storage),this.opened.then(()=>{this.archived&&(this.storage.requester=this.archive.request.bind(this.archive));let n=(s,a)=>{a.output=this.resources.substitute(s,a.url)};this.resources.settings.replacements=t||"blobUrl",this.resources.replacements().then(()=>this.resources.replaceCss()),this.storage.on("offline",()=>{this.url=new At("/",""),this.spine.hooks.serialize.register(n)}),this.storage.on("online",()=>{this.url=i,this.spine.hooks.serialize.deregister(n)})}),this.storage}coverUrl(){return this.loaded.cover.then(()=>this.cover?this.archived?this.archive.createUrl(this.cover):this.cover:null)}replacements(){return this.spine.hooks.serialize.register((e,t)=>{t.output=this.resources.substitute(e,t.url)}),this.resources.replacements().then(()=>this.resources.replaceCss())}getRange(e){var t=new Be(e),i=this.spine.get(t.spinePos),r=this.load.bind(this);return i?i.load(r).then(function(n){var s=t.toRange(i.document);return s}):new Promise((n,s)=>{s("CFI could not be found")})}key(e){var t=e||this.packaging.metadata.identifier||this.url.filename;return`epubjs:${_i}:${t}`}destroy(){this.opened=void 0,this.loading=void 0,this.loaded=void 0,this.ready=void 0,this.isOpen=!1,this.isRendered=!1,this.spine&&this.spine.destroy(),this.locations&&this.locations.destroy(),this.pageList&&this.pageList.destroy(),this.archive&&this.archive.destroy(),this.resources&&this.resources.destroy(),this.container&&this.container.destroy(),this.packaging&&this.packaging.destroy(),this.rendition&&this.rendition.destroy(),this.displayOptions&&this.displayOptions.destroy(),this.spine=void 0,this.locations=void 0,this.pageList=void 0,this.archive=void 0,this.resources=void 0,this.container=void 0,this.packaging=void 0,this.rendition=void 0,this.navigation=void 0,this.url=void 0,this.path=void 0,this.archived=!1}}St(Nr.prototype);function Bt(y,e){return new Nr(y,e)}Bt.VERSION=_i;typeof global<"u"&&(global.EPUBJS_VERSION=_i);Bt.Book=Nr;Bt.Rendition=kr;Bt.Contents=Sr;Bt.CFI=Be;Bt.utils=aa;var to=Object.defineProperty,io=(y,e,t)=>e in y?to(y,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):y[e]=t,Nt=(y,e,t)=>io(y,typeof e!="symbol"?e+"":e,t);const br={viewHolder:{position:"relative",height:"100%",width:"100%"},view:{height:"100%"}};class ro extends me.Component{constructor(e){super(e),Nt(this,"state",{isLoaded:!1,isError:!1,toc:[]}),Nt(this,"viewerRef",As.createRef()),Nt(this,"location"),Nt(this,"book"),Nt(this,"rendition"),Nt(this,"prevPage"),Nt(this,"nextPage"),Nt(this,"onLocationChange",t=>{const{location:i,locationChanged:r}=this.props,n=`${t.start}`;i!==n&&(this.location=n,r&&r(n))}),Nt(this,"handleKeyPress",t=>{this.props.handleKeyPress||(t.key==="ArrowRight"&&this.nextPage&&this.nextPage(),t.key==="ArrowLeft"&&this.prevPage&&this.prevPage())}),this.location=e.location,this.book=this.rendition=this.prevPage=this.nextPage=void 0}componentDidMount(){this.initBook(),document.addEventListener("keyup",this.handleKeyPress,!1)}initBook(){const{url:e,tocChanged:t,epubInitOptions:i}=this.props;this.book&&this.book.destroy(),this.book=Bt(e,i),this.book.on("openFailed",r=>{this.setState({isError:!0})}),this.book.loaded.navigation.then(({toc:r})=>{this.setState({isLoaded:!0,isError:!1,toc:r},()=>{t&&t(r),this.initReader()})})}componentWillUnmount(){this.book&&this.book.destroy(),this.book=this.rendition=this.prevPage=this.nextPage=void 0,document.removeEventListener("keyup",this.handleKeyPress,!1)}shouldComponentUpdate(e){return!this.state.isLoaded||e.location!==this.props.location||e.url!==this.props.url}componentDidUpdate(e){var t;e.location!==this.props.location&&this.location!==this.props.location&&((t=this.rendition)==null||t.display(this.props.location+"")),e.url!==this.props.url&&this.initBook()}initReader(){const{toc:e}=this.state,{location:t,epubOptions:i,getRendition:r}=this.props;if(this.viewerRef.current){const n=this.viewerRef.current;if(this.book){const s=this.book.renderTo(n,{width:"100%",height:"100%",...i});this.rendition=s,this.prevPage=()=>{s.prev()},this.nextPage=()=>{s.next()},this.registerEvents(),r&&r(s),typeof t=="string"||typeof t=="number"?s.display(t+""):e.length>0&&e[0].href?s.display(e[0].href):s.display()}}}registerEvents(){const{handleKeyPress:e,handleTextSelected:t}=this.props;this.rendition&&(this.rendition.on("locationChanged",this.onLocationChange),this.rendition.on("keyup",e||this.handleKeyPress),t&&this.rendition.on("selected",t))}renderBook(){const{epubViewStyles:e=br}=this.props;return we.jsx("div",{ref:this.viewerRef,style:e.view})}render(){const{isLoaded:e,isError:t}=this.state,{loadingView:i=null,errorView:r=null,epubViewStyles:n=br}=this.props;return we.jsxs("div",{style:n.viewHolder,children:[e&&this.renderBook(),!e&&!t&&i,!e&&t&&r]})}}async function no(){const{data:y}=await yi.get("/reader/settings/");return y}async function so(y){const{data:e}=await yi.patch("/reader/settings/",y);return e}async function ao(y){const{data:e}=await yi.get(`/books/ebooks/${y}/progress/`);return{id:y,book:y,current_chapter:e.last_page||1,current_position:e.current_position??0,percentage:e.current_position??0,epub_location:e.epub_location??"",updated_at:e.updated_at??""}}async function oo(y,e){return ds(y,{epub_location:e,current_position:0,percentage:0,current_chapter:0})}async function ds(y,e){const{data:t}=await yi.patch(`/books/ebooks/${y}/progress/`,{current_position:e.percentage??e.current_position??0,last_page:e.current_chapter??0,epub_location:e.epub_location??""});return{id:y,book:y,current_chapter:t.last_page||1,current_position:t.current_position??0,percentage:t.current_position??0,epub_location:t.epub_location??"",updated_at:t.updated_at??""}}const Ln={serif:'Georgia, "Times New Roman", serif',"sans-serif":'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',monospace:'ui-monospace, "Cascadia Code", monospace'},Bn="cloud-reader";function Vt(y){return typeof y=="string"&&y.startsWith("epubcfi(")}function uo(y,e){const t=e.indexOf("#"),i=t>=0?e.slice(t):"",r=t>=0?e.slice(0,t):e,n=[e,r];try{n.push(decodeURIComponent(r),encodeURI(r))}catch{}for(const u of n)if(y.spine.get(u))return u.includes("#")?u:u+i;const s=r.replace(/^\/+/,""),a=s.split("/").pop()??s;for(const u of y.spine.spineItems??[]){const h=u.href.replace(/^\/+/,""),d=h.split("/").pop()??h;if(h===s||h.endsWith(s)||s.endsWith(h)||d===a)return u.href+i}return e}function ps(y){return y*2}function lo(y,e){var r,n,s;const t=ps(e),i=y;i.settings&&(i.settings.gap=t),(r=i.manager)!=null&&r.settings&&(i.manager.settings.gap=t),(s=(n=i.manager)==null?void 0:n.isRendered)!=null&&s.call(n)&&i.manager.updateLayout&&i.manager.updateLayout()}function Pn(y,e){const t=Ln[e.font_family]??Ln.serif;lo(y,e.margin_width),y.themes.register(Bn,{body:{color:`${e.text_color} !important`,background:`${e.background_color} !important`,"font-family":`${t} !important`,"line-height":`${e.line_height} !important`}}),y.themes.select(Bn),y.themes.fontSize(`${e.font_size}px`)}async function Fn(y){var e;try{const t=await y.currentLocation(),i=(e=t==null?void 0:t.start)==null?void 0:e.href;if(i&&y.book.navigation){const r=y.book.navigation.get(i);if(r!=null&&r.label)return r.label}}catch{}return""}function ho(y,e,t){const[i,r]=me.useState(null),[n,s]=me.useState(0),[a,u]=me.useState(""),[h,d]=me.useState(null),[c,w]=me.useState(!0),[m,N]=me.useState(null),E=me.useRef(null),f=me.useRef(null),v=me.useRef(null),g=me.useRef(!1),D={flow:"paginated",manager:"default",spread:"none",width:"100%",height:"100%",gap:ps(e)};me.useEffect(()=>{let Y=null,X=!1;g.current=!1;async function ie(){w(!0),N(null);try{const[K,$]=await Promise.all([ao(y).catch(()=>null),jn.getEpubFile(y)]);if(X)return;Y=URL.createObjectURL($),r(Y),t&&Vt(t)?s(t):K!=null&&K.epub_location?(d(K),s(K.epub_location)):K&&d(K)}catch(K){X||N(K instanceof Error?K.message:"Failed to load EPUB")}finally{X||w(!1)}}return ie(),()=>{X=!0,Y&&URL.revokeObjectURL(Y),v.current&&clearTimeout(v.current)}},[y,t]);const L=me.useCallback(async(Y,X)=>{try{const ie=await ds(y,{epub_location:Y,percentage:X,current_position:X});d(ie)}catch{}},[y]),b=me.useCallback(async Y=>{try{const X=await oo(y,Y);d(X)}catch{}},[y]),S=me.useCallback(Y=>{var K;if(s(Y),!Vt(Y))return;const X=E.current;let ie=0;if((K=X==null?void 0:X.book)!=null&&K.locations){const $=X.book.locations.percentageFromCfi(Y);$!=null&&(ie=Math.round($*100))}if(X&&Fn(X).then($=>{$&&u($)}),!g.current&&ie<=0){g.current=!0,b(Y);return}v.current&&clearTimeout(v.current),v.current=setTimeout(()=>{L(Y,ie)},3e3)},[b,L]),R=me.useCallback(Y=>{Vt(Y)&&s(Y)},[]),I=me.useCallback(Y=>{const X=E.current;if(!X){s(Y);return}X.book.ready.then(()=>{const ie=uo(X.book,Y);return s(ie),X.display(ie)})},[]),k=me.useCallback(()=>{var $,ae;const Y=E.current;if(!Y||g.current)return;const X=($=Y.location)==null?void 0:$.start,ie=X==null?void 0:X.cfi;if(!ie||!Vt(ie))return;let K=0;if((ae=Y.book)!=null&&ae.locations){const pe=Y.book.locations.percentageFromCfi(ie);pe!=null&&(K=Math.round(pe*100))}K<=0&&(g.current=!0,b(ie))},[b]),O=me.useCallback(Y=>{var K,$,ae;const X=Y;E.current=Y;const ie=()=>{const pe=document.querySelector(".reader-epub-container");pe&&"resize"in Y&&Y.resize(pe.clientWidth,pe.clientHeight)};return(ae=($=(K=X.hooks)==null?void 0:K.content)==null?void 0:$.register)==null||ae.call($,pe=>{var ne;(ne=pe.document)!=null&&ne.body&&(pe.document.body.style.userSelect="text",pe.document.body.style.webkitUserSelect="text")}),X.book.ready.then(()=>X.book.locations.generate(1600)).then(ie).then(()=>Fn(X)).then(pe=>{pe&&u(pe)}).then(()=>k()),window.addEventListener("resize",ie),f.current&&Pn(Y,f.current),()=>window.removeEventListener("resize",ie)},[k]),G=me.useCallback(Y=>{f.current=Y,E.current&&Pn(E.current,Y)},[]),j=me.useCallback(()=>{var Y;(Y=E.current)==null||Y.next()},[]),Z=me.useCallback(()=>{var Y;(Y=E.current)==null||Y.prev()},[]);return{epubUrl:i,location:n,chapterTitle:a,progress:h,isLoading:c,error:m,handleLocationChanged:S,handleGetRendition:O,navigateToHref:I,jumpToCfi:R,goToNextPage:j,goToPrevPage:Z,applySettings:G,epubOptions:D,renditionRef:E}}function zn(y,e){var u,h,d,c,w,m,N,E,f,v,g,D,L,b,S;const t=((w=(c=(d=(h=(u=y.window)==null?void 0:u.getSelection)==null?void 0:h.call(u))==null?void 0:d.toString)==null?void 0:c.call(d))==null?void 0:w.trim())??((v=(f=(E=(N=(m=y.document)==null?void 0:m.getSelection)==null?void 0:N.call(m))==null?void 0:E.toString)==null?void 0:f.call(E))==null?void 0:v.trim())??"";if(!t)return null;const i=y.document.getSelection();if(!i||i.rangeCount===0)return null;const r=i.getRangeAt(0);let n="";try{n=((g=y.cfiFromRange)==null?void 0:g.call(y,r))??""}catch{n=""}if(!n||!Vt(n)){const R=(L=(D=e.location)==null?void 0:D.start)==null?void 0:L.cfi;if(R&&Vt(R))n=R;else return null}const s=r.getBoundingClientRect(),a=((S=(b=e.location)==null?void 0:b.start)==null?void 0:S.index)??0;return{epubCfi:n,locationText:t.slice(0,2e3),chapterIndex:typeof a=="number"?a:0,chapterTitle:"",anchorTop:s.bottom+8,anchorLeft:Math.min(s.left,window.innerWidth-280)}}function fo(y,e,t){const[i,r]=me.useState(null),n=me.useRef(e);n.current=e;const s=me.useCallback(()=>{r(null)},[]);return me.useEffect(()=>{var d,c,w;const a=y.current;if(!a)return;const u=m=>{if(!m){r(null);return}r({...m,chapterTitle:n.current})},h=(m,N)=>{u(zn(N,a))};return a.on&&a.on("selected",h),(w=(c=(d=a.hooks)==null?void 0:d.content)==null?void 0:c.register)==null||w.call(c,m=>{const N=()=>{window.setTimeout(()=>{u(zn(m,a))},10)};m.document.addEventListener("mouseup",N)}),()=>{var m;(m=a.off)==null||m.call(a,"selected",h)}},[y,t]),{pendingSelection:i,clearSelection:s}}const Mn={sepia:{background_color:"#f5f0eb",text_color:"#1a1a1a"},dark:{background_color:"#1a1a2e",text_color:"#e0e0e0"},light:{background_color:"#ffffff",text_color:"#1a1a1a"},paper:{background_color:"#e8e0d4",text_color:"#2c2c2c"}},Un={font_family:"serif",font_size:18,line_height:1.6,margin_width:16,background_color:"#f5f0eb",text_color:"#1a1a1a",brightness:100,orientation_lock:"auto",theme:"sepia",created_at:"",updated_at:""},co=600;function oi(y){const e=document.documentElement;e.style.setProperty("--reader-bg",y.background_color),e.style.setProperty("--reader-text",y.text_color),e.style.setProperty("--reader-margin",`${y.margin_width}px`),e.style.setProperty("--reader-brightness",`${y.brightness}%`)}function po(y,e){const t={...y,...e};return e.theme&&Mn[e.theme]&&Object.assign(t,Mn[e.theme]),t}function vo(){const[y,e]=me.useState(Un),[t,i]=me.useState(!0),[r,n]=me.useState(null),s=me.useRef({}),a=me.useRef(null),u=me.useRef(!1),h=me.useCallback(async()=>{const m={...s.current};if(s.current={},!(Object.keys(m).length===0||u.current)){u.current=!0;try{const N=await so(m);e(N),oi(N),n(null)}catch(N){n(N instanceof Error?N.message:"Failed to update reading settings")}finally{u.current=!1}}},[]),d=me.useCallback((m,N)=>{if(Object.assign(s.current,m),a.current&&clearTimeout(a.current),N){h();return}a.current=setTimeout(()=>{h()},co)},[h]);me.useEffect(()=>{let m=!1;return i(!0),no().then(N=>{m||(e(N),oi(N))}).catch(N=>{m||(n(N instanceof Error?N.message:"Failed to load reading settings"),oi(Un))}).finally(()=>{m||i(!1)}),()=>{m=!0,a.current&&clearTimeout(a.current)}},[]);const c=me.useCallback((m,N="debounced")=>{e(E=>{const f=po(E,m);return oi(f),f}),d(m,N==="immediate")},[d]),w=me.useCallback(async()=>{a.current&&clearTimeout(a.current),await h()},[h]);return{settings:y,isLoading:t,error:r,updateSettings:c,flushSettings:w}}function go({ebookId:y,selection:e,onClose:t,onSaved:i}){const{t:r}=wr(),{addMarker:n}=qn(),{showToast:s}=Ds(),[a,u]=me.useState(""),[h,d]=me.useState(!1),c=async()=>{d(!0);try{await n({ebook:y,epub_cfi:e.epubCfi,chapter_index:e.chapterIndex,chapter_title:e.chapterTitle,location_text:e.locationText,content:a.trim()}),s({message:a.trim()?r("annotations.markerSavedWithNote"):r("annotations.markerSaved"),variant:"success"}),i(),t()}catch(w){s({message:ks(w,r("annotations.saveFailed")),variant:"error"})}finally{d(!1)}};return we.jsxs(we.Fragment,{children:[we.jsx("div",{className:"selection-popover-backdrop",onClick:t,"aria-hidden":!0}),we.jsxs("div",{className:"selection-popover",style:{top:e.anchorTop,left:e.anchorLeft},role:"dialog","aria-label":r("annotations.saveMarker"),children:[we.jsxs("p",{className:"selection-popover-quote",children:["“",e.locationText.length>120?`${e.locationText.slice(0,120)}…`:e.locationText,"”"]}),we.jsx("textarea",{className:"selection-popover-input",value:a,onChange:w=>u(w.target.value),placeholder:r("annotations.thoughtPlaceholder"),rows:3,autoFocus:!0}),we.jsxs("div",{className:"selection-popover-actions",children:[we.jsx("button",{type:"button",className:"btn btn-sm",onClick:c,disabled:h,children:r(h?"common.saving":"annotations.saveMarker")}),we.jsx("button",{type:"button",className:"btn btn-sm btn-secondary",onClick:t,disabled:h,children:r("common.cancel")})]})]})]})}function mo({ebookId:y,isOpen:e,onClose:t,onGoToPassage:i}){const{t:r}=wr(),{markers:n,loadBookmarks:s,removeBookmark:a,state:u}=qn();return me.useEffect(()=>{e&&s(y)},[e,y,s]),e?we.jsxs(we.Fragment,{children:[we.jsx("div",{className:"reader-panel-backdrop",onClick:t,"aria-hidden":!0}),we.jsxs("aside",{className:"reader-side-panel reader-markers-panel",role:"dialog","aria-label":r("annotations.inBookPanel"),children:[we.jsxs("header",{className:"reader-panel-header",children:[we.jsx("h2",{children:r("annotations.inBookPanel")}),we.jsx("button",{type:"button",className:"reader-panel-close",onClick:t,"aria-label":r("annotations.close"),children:"×"})]}),we.jsx("div",{className:"reader-panel-body",children:u.bookmarksLoading?we.jsx("p",{className:"annotations-loading",children:r("annotations.loading")}):n.length===0?we.jsx("p",{className:"annotations-empty",children:r("annotations.selectTextHint")}):we.jsx("ul",{className:"marker-thread-list",children:n.map(h=>we.jsxs("li",{className:"marker-thread",children:[h.chapter_title&&we.jsx("span",{className:"marker-thread-chapter",children:h.chapter_title}),h.location_text&&we.jsxs("blockquote",{className:"annotation-quote",children:["“",h.location_text,"”"]}),h.content?we.jsx("p",{className:"marker-thread-thought",children:h.content}):we.jsx("span",{className:"annotation-kind-badge bookmark-badge",children:r("annotations.bookmarkOnly")}),we.jsxs("div",{className:"annotation-actions",children:[we.jsx("button",{type:"button",className:"btn btn-sm",onClick:()=>i(h),children:r("annotations.goToPassage")}),we.jsx("button",{type:"button",className:"btn btn-sm btn-danger",onClick:()=>a(h.id),children:r("common.delete")})]})]},h.id))})})]})]}):null}const yo=me.lazy(()=>_r(()=>import("./ReaderToolbar-wmOf7G3U.js"),__vite__mapDeps([0,1,2]))),bo=me.lazy(()=>_r(()=>import("./TableOfContents-BqoUCglr.js"),__vite__mapDeps([3,1,2]))),wo=me.lazy(()=>_r(()=>import("./ReadingSettingsPanel-Bt3-VHc7.js"),__vite__mapDeps([4,1,2])));function Do(){var ke;const{t:y}=wr(),{id:e}=Cs(),t=Ss(),i=Ts(),r=Number(e),n=(ke=i.state)==null?void 0:ke.epubLocation,[s,a]=me.useState(null),[u,h]=me.useState(!0),[d,c]=me.useState(null),[w,m]=me.useState(!1),[N,E]=me.useState(!1),[f,v]=me.useState(!1),[g,D]=me.useState([]),[L,b]=me.useState(0),{settings:S,updateSettings:R,flushSettings:I}=vo(),{epubUrl:k,location:O,chapterTitle:G,progress:j,isLoading:Z,error:Y,handleLocationChanged:X,handleGetRendition:ie,navigateToHref:K,jumpToCfi:$,goToNextPage:ae,goToPrevPage:pe,applySettings:ne,epubOptions:ce,renditionRef:Ce}=ho(r,S.margin_width,n),{pendingSelection:ye,clearSelection:Te}=fo(Ce,G,L);me.useEffect(()=>{!r||Number.isNaN(r)||(h(!0),c(null),jn.getEBook(r).then(ve=>{if(ve.format!=="epub"){c(y("reader.epubOnly")),a(null);return}a(ve)}).catch(()=>c(y("reader.loadBookFailed"))).finally(()=>h(!1)))},[r,y]),me.useEffect(()=>{ne(S)},[S,ne]),me.useEffect(()=>{const ve=document.documentElement;S.orientation_lock!=="auto"?ve.style.setProperty("--reader-orientation",S.orientation_lock==="portrait"?"portrait":"landscape"):ve.style.removeProperty("--reader-orientation")},[S.orientation_lock]);const We=me.useCallback(ve=>{D(ve)},[]),Fe=me.useCallback(ve=>{K(ve)},[K]),be=me.useCallback(ve=>{ie(ve),b(Ie=>Ie+1)},[ie]),Pe=me.useCallback(ve=>{$(ve.epub_cfi),v(!1)},[$]),Ue=u||Z,xe=d??Y;return Ue?we.jsxs("div",{className:"reader-loading",children:[we.jsx("div",{className:"spinner"}),we.jsx("p",{children:y("reader.loading")})]}):xe||!k?we.jsxs("div",{className:"reader-loading",children:[we.jsx("p",{className:"reader-error",children:xe??y("reader.unableToOpen")}),we.jsx("button",{type:"button",className:"back-button",onClick:()=>t("/"),children:y("reader.backToLibrary")})]}):we.jsx(me.Suspense,{fallback:we.jsx("div",{className:"reader-loading",children:we.jsx("div",{className:"spinner"})}),children:we.jsxs("div",{className:"reader-container","data-theme":S.theme,children:[we.jsx(yo,{bookTitle:(s==null?void 0:s.title)??y("reader.reading"),chapterTitle:G||y("reader.reading"),progress:j,onBack:()=>t("/"),onToggleToc:()=>m(ve=>!ve),onToggleSettings:()=>E(ve=>!ve),onToggleMarkers:()=>v(ve=>!ve)}),we.jsx(bo,{items:g,isOpen:w,onClose:()=>m(!1),onNavigate:Fe}),we.jsx(mo,{ebookId:r,isOpen:f,onClose:()=>v(!1),onGoToPassage:Pe}),we.jsx(wo,{settings:S,isOpen:N,onClose:()=>E(!1),onUpdate:R,onFlush:I}),ye&&we.jsx(go,{ebookId:r,selection:ye,onClose:Te,onSaved:Te}),we.jsxs("main",{className:"reader-epub-container",children:[we.jsx(ro,{url:k,location:O,locationChanged:X,tocChanged:We,getRendition:ve=>be(ve),epubInitOptions:{openAs:"epub"},epubOptions:ce,epubViewStyles:br,loadingView:we.jsx("div",{className:"reader-loading",children:we.jsx("div",{className:"spinner"})})}),we.jsx("button",{type:"button",className:"reader-page-nav reader-page-nav--prev",onClick:pe,"aria-label":y("reader.prevPage"),children:"‹"}),we.jsx("button",{type:"button",className:"reader-page-nav reader-page-nav--next",onClick:ae,"aria-label":y("reader.nextPage"),children:"›"})]})]})})}export{Do as default}; diff --git a/frontend/dist/assets/Settings-DkxftMXf.js b/frontend/dist/assets/Settings-DkxftMXf.js new file mode 100644 index 0000000..0b8b072 --- /dev/null +++ b/frontend/dist/assets/Settings-DkxftMXf.js @@ -0,0 +1 @@ +import{X as j,h as m,r as i,j as e}from"./index-BQiEVoRj.js";import{b as g}from"./books-BG7wMiRx.js";const S=[{value:"#ffffff",labelKey:"settings.bgWhite"},{value:"#f4e4c1",labelKey:"settings.bgSepia"},{value:"#1a1a2e",labelKey:"settings.bgDark"},{value:"#c7edcc",labelKey:"settings.bgGreen"}];function W(){const{t,language:u,setLanguage:p}=j(),x=m(),[s,a]=i.useState(null),[h,y]=i.useState(!0),[o,c]=i.useState(!1),[f,r]=i.useState(null),[v,d]=i.useState(!1);i.useEffect(()=>{(async()=>{try{const l=await g.getSettings();a(l)}catch(l){r(l instanceof Error?l.message:t("settings.loadFailed"))}finally{y(!1)}})()},[t]);const b=async()=>{if(s){c(!0),r(null),d(!1);try{await g.updateSettings(s),d(!0),setTimeout(()=>d(!1),2e3)}catch(n){r(n instanceof Error?n.message:t("settings.saveFailed"))}finally{c(!1)}}};return h?e.jsx("div",{style:{maxWidth:500,margin:"0 auto",padding:16,minHeight:"100vh",background:"#f8f9fa"},children:e.jsx("p",{children:t("settings.loading")})}):e.jsxs("div",{style:{maxWidth:500,margin:"0 auto",padding:16,minHeight:"100vh",background:"#f8f9fa"},children:[e.jsxs("header",{style:{display:"flex",alignItems:"center",gap:12,marginBottom:24,padding:"16px 0",borderBottom:"1px solid #eee"},children:[e.jsxs("button",{onClick:()=>x("/"),style:{padding:"8px 16px",borderRadius:6,border:"1px solid #ddd",background:"#fff",cursor:"pointer",fontSize:14},children:["← ",t("common.back")]}),e.jsx("h1",{style:{fontSize:24,fontWeight:700,color:"#1a1a2e",margin:0},children:t("settings.title")})]}),e.jsxs("div",{style:{display:"flex",flexDirection:"column",gap:24},children:[f&&e.jsx("div",{style:{background:"#fde8e8",padding:12,borderRadius:6,color:"#e74c3c",fontSize:14},children:f}),v&&e.jsx("div",{style:{background:"#d4edda",padding:12,borderRadius:6,color:"#155724",fontSize:14},children:t("settings.saved")}),e.jsxs("div",{style:{display:"flex",flexDirection:"column",gap:8},children:[e.jsx("label",{style:{fontSize:14,fontWeight:600,color:"#555"},children:t("settings.uiLanguage")}),e.jsxs("select",{value:u,onChange:n=>p(n.target.value),style:{padding:"10px 12px",borderRadius:6,border:"1px solid #ddd",fontSize:16,background:"#fff",outline:"none"},children:[e.jsx("option",{value:"en-US",children:t("settings.uiLanguageEn")}),e.jsx("option",{value:"es-ES",children:t("settings.uiLanguageEs")})]})]}),s&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{style:{display:"flex",flexDirection:"column",gap:8},children:[e.jsx("label",{style:{fontSize:14,fontWeight:600,color:"#555"},children:t("settings.fontSize",{size:String(s.font_size)})}),e.jsx("input",{type:"range",min:12,max:36,value:s.font_size,onChange:n=>a({...s,font_size:Number(n.target.value)}),style:{width:"100%",cursor:"pointer"}}),e.jsxs("div",{style:{display:"flex",justifyContent:"space-between",fontSize:12,color:"#999"},children:[e.jsx("span",{children:"12px"}),e.jsx("span",{children:"36px"})]})]}),e.jsxs("div",{style:{display:"flex",flexDirection:"column",gap:8},children:[e.jsx("label",{style:{fontSize:14,fontWeight:600,color:"#555"},children:t("settings.fontStyle")}),e.jsxs("select",{value:s.font_style,onChange:n=>a({...s,font_style:n.target.value}),style:{padding:"10px 12px",borderRadius:6,border:"1px solid #ddd",fontSize:16,background:"#fff",outline:"none"},children:[e.jsx("option",{value:"sans-serif",children:t("settings.fontSans")}),e.jsx("option",{value:"serif",children:t("settings.fontSerif")}),e.jsx("option",{value:"monospace",children:t("settings.fontMonospace")})]})]}),e.jsxs("div",{style:{display:"flex",flexDirection:"column",gap:8},children:[e.jsx("label",{style:{fontSize:14,fontWeight:600,color:"#555"},children:t("settings.backgroundColor")}),e.jsx("div",{style:{display:"flex",gap:12,flexWrap:"wrap"},children:S.map(n=>e.jsx("button",{onClick:()=>a({...s,background_color:n.value}),style:{width:48,height:48,borderRadius:"50%",cursor:"pointer",display:"flex",alignItems:"center",justifyContent:"center",background:n.value,border:s.background_color===n.value?"3px solid #1a1a2e":"3px solid #ddd"},title:t(n.labelKey),children:s.background_color===n.value&&e.jsx("span",{style:{fontSize:20,fontWeight:700,color:n.value==="#ffffff"||n.value==="#f4e4c1"?"#1a1a2e":"#fff"},children:"✓"})},n.value))})]})]}),e.jsx("button",{onClick:b,disabled:o,style:{padding:"12px 24px",borderRadius:8,border:"none",background:"#1a1a2e",color:"#fff",fontSize:16,fontWeight:600,cursor:"pointer",opacity:o?.6:1,marginTop:8},children:t(o?"common.saving":"settings.saveSettings")})]})]})}export{W as SettingsPage}; diff --git a/frontend/dist/assets/TableOfContents-BqoUCglr.js b/frontend/dist/assets/TableOfContents-BqoUCglr.js new file mode 100644 index 0000000..53db498 --- /dev/null +++ b/frontend/dist/assets/TableOfContents-BqoUCglr.js @@ -0,0 +1 @@ +import{X as i,j as e}from"./index-BQiEVoRj.js";function o({item:s,depth:a,onNavigate:t}){var c;return e.jsxs(e.Fragment,{children:[e.jsx("button",{type:"button",className:"toc-item",style:{paddingLeft:`${16+a*14}px`},onClick:()=>t(s.href),children:e.jsx("span",{className:"toc-item-title",children:s.label})}),(c=s.subitems)==null?void 0:c.map(r=>e.jsx(o,{item:r,depth:a+1,onNavigate:t},r.href))]})}function x({items:s,isOpen:a,onClose:t,onNavigate:c}){const{t:r}=i(),l=n=>{c(n),t()};return e.jsxs(e.Fragment,{children:[a&&e.jsx("div",{className:"toc-overlay",onClick:t,onKeyDown:n=>{n.key==="Escape"&&t()},role:"presentation"}),e.jsxs("aside",{className:`toc-drawer ${a?"toc-drawer--open":""}`,children:[e.jsxs("div",{className:"toc-header",children:[e.jsx("h2",{className:"toc-title",children:r("reader.tocTitle")}),e.jsx("button",{type:"button",className:"toc-close-btn",onClick:t,"aria-label":r("reader.closeTocAria"),children:e.jsxs("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[e.jsx("line",{x1:"18",y1:"6",x2:"6",y2:"18"}),e.jsx("line",{x1:"6",y1:"6",x2:"18",y2:"18"})]})})]}),e.jsx("nav",{className:"toc-list",children:s.length===0?e.jsx("p",{className:"toc-empty",children:r("reader.tocEmpty")}):s.map(n=>e.jsx(o,{item:n,depth:0,onNavigate:l},n.href))})]})]})}export{x as default}; diff --git a/frontend/dist/assets/books-BG7wMiRx.js b/frontend/dist/assets/books-BG7wMiRx.js new file mode 100644 index 0000000..b9d76db --- /dev/null +++ b/frontend/dist/assets/books-BG7wMiRx.js @@ -0,0 +1 @@ +import{a}from"./index-BQiEVoRj.js";const i={async getEBooks(){const{data:t}=await a.get("/books/ebooks/");return Array.isArray(t)?t:t.results??[]},async getEBook(t){const{data:o}=await a.get(`/books/ebooks/${t}/`);return o},async getEpubFile(t){const{data:o}=await a.get(`/books/ebooks/${t}/file/`,{responseType:"blob"});return o},async searchBooks(t={}){const{data:o}=await a.get("/books/",{params:t});return Array.isArray(o)?{count:o.length,results:o}:{count:o.count??0,results:o.results??[]}},async getBook(t){const{data:o}=await a.get(`/books/${t}/`);return o},async getGenres(){const{data:t}=await a.get("/books/genres/");return t},async getAuthors(){const{data:t}=await a.get("/books/authors/");return t},async uploadEBook(t,o,s,n){const e=new FormData;e.append("file",t),e.append("title",o),s&&e.append("author",s),n&&e.append("cover_image",n);const{data:r}=await a.post("/books/ebooks/",e,{headers:{"Content-Type":"multipart/form-data"}});return r},async deleteEBook(t){await a.delete(`/books/ebooks/${t}/`)},async enrichEBookMetadata(t){const{data:o}=await a.post(`/books/ebooks/${t}/enrich-metadata/`);return o},async processEBook(t){const{data:o}=await a.post(`/books/ebooks/${t}/process/`);return o},async getToc(t){const{data:o}=await a.get(`/books/ebooks/${t}/toc/`);return o},async getContent(t,o){const{data:s}=await a.get(`/books/ebooks/${t}/content/?page=${o}`);return s},async getProgress(t){const{data:o}=await a.get(`/books/ebooks/${t}/progress/`);return o},async updateProgress(t,o){const{data:s}=await a.patch(`/books/ebooks/${t}/progress/`,o);return s},async getSettings(){const{data:t}=await a.get("/books/settings/");return t},async updateSettings(t){const{data:o}=await a.patch("/books/settings/",t);return o}};export{i as b}; diff --git a/frontend/dist/assets/errors-bLl0IgZb.js b/frontend/dist/assets/errors-bLl0IgZb.js new file mode 100644 index 0000000..8c7e9a9 --- /dev/null +++ b/frontend/dist/assets/errors-bLl0IgZb.js @@ -0,0 +1 @@ +import{b as f}from"./index-BQiEVoRj.js";function i(t){if(t==null)return[];if(typeof t=="string")return[t];if(Array.isArray(t))return t.flatMap(i);if(typeof t=="object"){const s=t;if("detail"in s){const e=i(s.detail);if(e.length>0)return e}const r=[];for(const[e,o]of Object.entries(s))if(e!=="detail")for(const n of i(o))r.push(e==="non_field_errors"?n:`${e}: ${n}`);return r}return[]}function g(t,s="Something went wrong"){var r,e;if(f.isAxiosError(t)){const o=(r=t.response)==null?void 0:r.data;if(o!==void 0){const n=i(o);if(n.length>0)return n.join(". ")}return(e=t.response)!=null&&e.status&&t.message.startsWith("Request failed")?s:t.message||s}return t instanceof Error?t.message:s}export{g}; diff --git a/frontend/dist/assets/index-BQiEVoRj.js b/frontend/dist/assets/index-BQiEVoRj.js new file mode 100644 index 0000000..c58cea5 --- /dev/null +++ b/frontend/dist/assets/index-BQiEVoRj.js @@ -0,0 +1,70 @@ +const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/Library-Ci_rxC-B.js","assets/books-BG7wMiRx.js","assets/useMediaQuery-DwhAHSnz.js","assets/errors-bLl0IgZb.js","assets/Library-2D9sFJJ7.css","assets/BookDetailPage-B8jJgony.js","assets/AddBook-DiryQGMk.js","assets/Settings-DkxftMXf.js","assets/ReadingPage-KSOW0yWe.js","assets/ReadingPage-BP5NX83v.css","assets/AuthPage-Dbb0UvrV.js"])))=>i.map(i=>d[i]); +(function(){const i=document.createElement("link").relList;if(i&&i.supports&&i.supports("modulepreload"))return;for(const s of document.querySelectorAll('link[rel="modulepreload"]'))o(s);new MutationObserver(s=>{for(const d of s)if(d.type==="childList")for(const h of d.addedNodes)h.tagName==="LINK"&&h.rel==="modulepreload"&&o(h)}).observe(document,{childList:!0,subtree:!0});function c(s){const d={};return s.integrity&&(d.integrity=s.integrity),s.referrerPolicy&&(d.referrerPolicy=s.referrerPolicy),s.crossOrigin==="use-credentials"?d.credentials="include":s.crossOrigin==="anonymous"?d.credentials="omit":d.credentials="same-origin",d}function o(s){if(s.ep)return;s.ep=!0;const d=c(s);fetch(s.href,d)}})();var eE=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function Fm(l){return l&&l.__esModule&&Object.prototype.hasOwnProperty.call(l,"default")?l.default:l}var gc={exports:{}},Il={};/** + * @license React + * react-jsx-runtime.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var cm;function Sv(){if(cm)return Il;cm=1;var l=Symbol.for("react.transitional.element"),i=Symbol.for("react.fragment");function c(o,s,d){var h=null;if(d!==void 0&&(h=""+d),s.key!==void 0&&(h=""+s.key),"key"in s){d={};for(var p in s)p!=="key"&&(d[p]=s[p])}else d=s;return s=d.ref,{$$typeof:l,type:o,key:h,ref:s!==void 0?s:null,props:d}}return Il.Fragment=i,Il.jsx=c,Il.jsxs=c,Il}var sm;function Ev(){return sm||(sm=1,gc.exports=Sv()),gc.exports}var W=Ev(),vc={exports:{}},ne={};/** + * @license React + * react.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var fm;function _v(){if(fm)return ne;fm=1;var l=Symbol.for("react.transitional.element"),i=Symbol.for("react.portal"),c=Symbol.for("react.fragment"),o=Symbol.for("react.strict_mode"),s=Symbol.for("react.profiler"),d=Symbol.for("react.consumer"),h=Symbol.for("react.context"),p=Symbol.for("react.forward_ref"),g=Symbol.for("react.suspense"),y=Symbol.for("react.memo"),v=Symbol.for("react.lazy"),S=Symbol.for("react.activity"),M=Symbol.iterator;function Y(E){return E===null||typeof E!="object"?null:(E=M&&E[M]||E["@@iterator"],typeof E=="function"?E:null)}var q={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},N=Object.assign,D={};function U(E,j,G){this.props=E,this.context=j,this.refs=D,this.updater=G||q}U.prototype.isReactComponent={},U.prototype.setState=function(E,j){if(typeof E!="object"&&typeof E!="function"&&E!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,E,j,"setState")},U.prototype.forceUpdate=function(E){this.updater.enqueueForceUpdate(this,E,"forceUpdate")};function Q(){}Q.prototype=U.prototype;function X(E,j,G){this.props=E,this.context=j,this.refs=D,this.updater=G||q}var F=X.prototype=new Q;F.constructor=X,N(F,U.prototype),F.isPureReactComponent=!0;var ae=Array.isArray;function pe(){}var te={H:null,A:null,T:null,S:null},Ce=Object.prototype.hasOwnProperty;function Qe(E,j,G){var V=G.ref;return{$$typeof:l,type:E,key:j,ref:V!==void 0?V:null,props:G}}function Ze(E,j){return Qe(E.type,j,E.props)}function Ye(E){return typeof E=="object"&&E!==null&&E.$$typeof===l}function He(E){var j={"=":"=0",":":"=2"};return"$"+E.replace(/[=:]/g,function(G){return j[G]})}var ht=/\/+/g;function Ue(E,j){return typeof E=="object"&&E!==null&&E.key!=null?He(""+E.key):j.toString(36)}function xe(E){switch(E.status){case"fulfilled":return E.value;case"rejected":throw E.reason;default:switch(typeof E.status=="string"?E.then(pe,pe):(E.status="pending",E.then(function(j){E.status==="pending"&&(E.status="fulfilled",E.value=j)},function(j){E.status==="pending"&&(E.status="rejected",E.reason=j)})),E.status){case"fulfilled":return E.value;case"rejected":throw E.reason}}throw E}function B(E,j,G,V,P){var ie=typeof E;(ie==="undefined"||ie==="boolean")&&(E=null);var ce=!1;if(E===null)ce=!0;else switch(ie){case"bigint":case"string":case"number":ce=!0;break;case"object":switch(E.$$typeof){case l:case i:ce=!0;break;case v:return ce=E._init,B(ce(E._payload),j,G,V,P)}}if(ce)return P=P(E),ce=V===""?"."+Ue(E,0):V,ae(P)?(G="",ce!=null&&(G=ce.replace(ht,"$&/")+"/"),B(P,j,G,"",function(Vt){return Vt})):P!=null&&(Ye(P)&&(P=Ze(P,G+(P.key==null||E&&E.key===P.key?"":(""+P.key).replace(ht,"$&/")+"/")+ce)),j.push(P)),1;ce=0;var le=V===""?".":V+":";if(ae(E))for(var _e=0;_e>>1,oe=B[se];if(0>>1;ses(G,I))Vs(P,G)?(B[se]=P,B[V]=I,se=V):(B[se]=G,B[j]=I,se=j);else if(Vs(P,I))B[se]=P,B[V]=I,se=V;else break e}}return k}function s(B,k){var I=B.sortIndex-k.sortIndex;return I!==0?I:B.id-k.id}if(l.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var d=performance;l.unstable_now=function(){return d.now()}}else{var h=Date,p=h.now();l.unstable_now=function(){return h.now()-p}}var g=[],y=[],v=1,S=null,M=3,Y=!1,q=!1,N=!1,D=!1,U=typeof setTimeout=="function"?setTimeout:null,Q=typeof clearTimeout=="function"?clearTimeout:null,X=typeof setImmediate<"u"?setImmediate:null;function F(B){for(var k=c(y);k!==null;){if(k.callback===null)o(y);else if(k.startTime<=B)o(y),k.sortIndex=k.expirationTime,i(g,k);else break;k=c(y)}}function ae(B){if(N=!1,F(B),!q)if(c(g)!==null)q=!0,pe||(pe=!0,He());else{var k=c(y);k!==null&&xe(ae,k.startTime-B)}}var pe=!1,te=-1,Ce=5,Qe=-1;function Ze(){return D?!0:!(l.unstable_now()-QeB&&Ze());){var se=S.callback;if(typeof se=="function"){S.callback=null,M=S.priorityLevel;var oe=se(S.expirationTime<=B);if(B=l.unstable_now(),typeof oe=="function"){S.callback=oe,F(B),k=!0;break t}S===c(g)&&o(g),F(B)}else o(g);S=c(g)}if(S!==null)k=!0;else{var E=c(y);E!==null&&xe(ae,E.startTime-B),k=!1}}break e}finally{S=null,M=I,Y=!1}k=void 0}}finally{k?He():pe=!1}}}var He;if(typeof X=="function")He=function(){X(Ye)};else if(typeof MessageChannel<"u"){var ht=new MessageChannel,Ue=ht.port2;ht.port1.onmessage=Ye,He=function(){Ue.postMessage(null)}}else He=function(){U(Ye,0)};function xe(B,k){te=U(function(){B(l.unstable_now())},k)}l.unstable_IdlePriority=5,l.unstable_ImmediatePriority=1,l.unstable_LowPriority=4,l.unstable_NormalPriority=3,l.unstable_Profiling=null,l.unstable_UserBlockingPriority=2,l.unstable_cancelCallback=function(B){B.callback=null},l.unstable_forceFrameRate=function(B){0>B||125se?(B.sortIndex=I,i(y,B),c(g)===null&&B===c(y)&&(N?(Q(te),te=-1):N=!0,xe(ae,I-se))):(B.sortIndex=oe,i(g,B),q||Y||(q=!0,pe||(pe=!0,He()))),B},l.unstable_shouldYield=Ze,l.unstable_wrapCallback=function(B){var k=M;return function(){var I=M;M=k;try{return B.apply(this,arguments)}finally{M=I}}}})(Ec)),Ec}var mm;function Rv(){return mm||(mm=1,Sc.exports=Tv()),Sc.exports}var _c={exports:{}},tt={};/** + * @license React + * react-dom.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var ym;function Ov(){if(ym)return tt;ym=1;var l=Vc();function i(g){var y="https://react.dev/errors/"+g;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(l)}catch(i){console.error(i)}}return l(),_c.exports=Ov(),_c.exports}/** + * @license React + * react-dom-client.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var gm;function zv(){if(gm)return eu;gm=1;var l=Rv(),i=Vc(),c=Cv();function o(e){var t="https://react.dev/errors/"+e;if(1oe||(e.current=se[oe],se[oe]=null,oe--)}function G(e,t){oe++,se[oe]=e.current,e.current=t}var V=E(null),P=E(null),ie=E(null),ce=E(null);function le(e,t){switch(G(ie,t),G(P,e),G(V,null),t.nodeType){case 9:case 11:e=(e=t.documentElement)&&(e=e.namespaceURI)?Nh(e):0;break;default:if(e=t.tagName,t=t.namespaceURI)t=Nh(t),e=Uh(t,e);else switch(e){case"svg":e=1;break;case"math":e=2;break;default:e=0}}j(V),G(V,e)}function _e(){j(V),j(P),j(ie)}function Vt(e){e.memoizedState!==null&&G(ce,e);var t=V.current,n=Uh(t,e.type);t!==n&&(G(P,e),G(V,n))}function Wt(e){P.current===e&&(j(V),j(P)),ce.current===e&&(j(ce),Fl._currentValue=I)}var ya,du;function mt(e){if(ya===void 0)try{throw Error()}catch(n){var t=n.stack.trim().match(/\n( *(at )?)/);ya=t&&t[1]||"",du=-1)":-1u||b[a]!==C[u]){var w=` +`+b[a].replace(" at new "," at ");return e.displayName&&w.includes("")&&(w=w.replace("",e.displayName)),w}while(1<=a&&0<=u);break}}}finally{Ii=!1,Error.prepareStackTrace=n}return(n=e?e.displayName||e.name:"")?mt(n):""}function Wy(e,t){switch(e.tag){case 26:case 27:case 5:return mt(e.type);case 16:return mt("Lazy");case 13:return e.child!==t&&t!==null?mt("Suspense Fallback"):mt("Suspense");case 19:return mt("SuspenseList");case 0:case 15:return er(e.type,!1);case 11:return er(e.type.render,!1);case 1:return er(e.type,!0);case 31:return mt("Activity");default:return""}}function os(e){try{var t="",n=null;do t+=Wy(e,n),n=e,e=e.return;while(e);return t}catch(a){return` +Error generating stack: `+a.message+` +`+a.stack}}var tr=Object.prototype.hasOwnProperty,nr=l.unstable_scheduleCallback,ar=l.unstable_cancelCallback,Py=l.unstable_shouldYield,Iy=l.unstable_requestPaint,yt=l.unstable_now,ep=l.unstable_getCurrentPriorityLevel,cs=l.unstable_ImmediatePriority,ss=l.unstable_UserBlockingPriority,hu=l.unstable_NormalPriority,tp=l.unstable_LowPriority,fs=l.unstable_IdlePriority,np=l.log,ap=l.unstable_setDisableYieldValue,rl=null,pt=null;function bn(e){if(typeof np=="function"&&ap(e),pt&&typeof pt.setStrictMode=="function")try{pt.setStrictMode(rl,e)}catch{}}var gt=Math.clz32?Math.clz32:ip,lp=Math.log,up=Math.LN2;function ip(e){return e>>>=0,e===0?32:31-(lp(e)/up|0)|0}var mu=256,yu=262144,pu=4194304;function Vn(e){var t=e&42;if(t!==0)return t;switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return e&261888;case 262144:case 524288:case 1048576:case 2097152:return e&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return e&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return e}}function gu(e,t,n){var a=e.pendingLanes;if(a===0)return 0;var u=0,r=e.suspendedLanes,f=e.pingedLanes;e=e.warmLanes;var m=a&134217727;return m!==0?(a=m&~r,a!==0?u=Vn(a):(f&=m,f!==0?u=Vn(f):n||(n=m&~e,n!==0&&(u=Vn(n))))):(m=a&~r,m!==0?u=Vn(m):f!==0?u=Vn(f):n||(n=a&~e,n!==0&&(u=Vn(n)))),u===0?0:t!==0&&t!==u&&(t&r)===0&&(r=u&-u,n=t&-t,r>=n||r===32&&(n&4194048)!==0)?t:u}function ol(e,t){return(e.pendingLanes&~(e.suspendedLanes&~e.pingedLanes)&t)===0}function rp(e,t){switch(e){case 1:case 2:case 4:case 8:case 64:return t+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return t+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function ds(){var e=pu;return pu<<=1,(pu&62914560)===0&&(pu=4194304),e}function lr(e){for(var t=[],n=0;31>n;n++)t.push(e);return t}function cl(e,t){e.pendingLanes|=t,t!==268435456&&(e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0)}function op(e,t,n,a,u,r){var f=e.pendingLanes;e.pendingLanes=n,e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0,e.expiredLanes&=n,e.entangledLanes&=n,e.errorRecoveryDisabledLanes&=n,e.shellSuspendCounter=0;var m=e.entanglements,b=e.expirationTimes,C=e.hiddenUpdates;for(n=f&~n;0"u")return null;try{return e.activeElement||e.body}catch{return e.body}}var mp=/[\n"\\]/g;function Ct(e){return e.replace(mp,function(t){return"\\"+t.charCodeAt(0).toString(16)+" "})}function sr(e,t,n,a,u,r,f,m){e.name="",f!=null&&typeof f!="function"&&typeof f!="symbol"&&typeof f!="boolean"?e.type=f:e.removeAttribute("type"),t!=null?f==="number"?(t===0&&e.value===""||e.value!=t)&&(e.value=""+Ot(t)):e.value!==""+Ot(t)&&(e.value=""+Ot(t)):f!=="submit"&&f!=="reset"||e.removeAttribute("value"),t!=null?fr(e,f,Ot(t)):n!=null?fr(e,f,Ot(n)):a!=null&&e.removeAttribute("value"),u==null&&r!=null&&(e.defaultChecked=!!r),u!=null&&(e.checked=u&&typeof u!="function"&&typeof u!="symbol"),m!=null&&typeof m!="function"&&typeof m!="symbol"&&typeof m!="boolean"?e.name=""+Ot(m):e.removeAttribute("name")}function Rs(e,t,n,a,u,r,f,m){if(r!=null&&typeof r!="function"&&typeof r!="symbol"&&typeof r!="boolean"&&(e.type=r),t!=null||n!=null){if(!(r!=="submit"&&r!=="reset"||t!=null)){cr(e);return}n=n!=null?""+Ot(n):"",t=t!=null?""+Ot(t):n,m||t===e.value||(e.value=t),e.defaultValue=t}a=a??u,a=typeof a!="function"&&typeof a!="symbol"&&!!a,e.checked=m?e.checked:!!a,e.defaultChecked=!!a,f!=null&&typeof f!="function"&&typeof f!="symbol"&&typeof f!="boolean"&&(e.name=f),cr(e)}function fr(e,t,n){t==="number"&&Su(e.ownerDocument)===e||e.defaultValue===""+n||(e.defaultValue=""+n)}function Ea(e,t,n,a){if(e=e.options,t){t={};for(var u=0;u"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),pr=!1;if(en)try{var hl={};Object.defineProperty(hl,"passive",{get:function(){pr=!0}}),window.addEventListener("test",hl,hl),window.removeEventListener("test",hl,hl)}catch{pr=!1}var En=null,gr=null,_u=null;function Ns(){if(_u)return _u;var e,t=gr,n=t.length,a,u="value"in En?En.value:En.textContent,r=u.length;for(e=0;e=pl),js=" ",qs=!1;function Ys(e,t){switch(e){case"keyup":return Xp.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function Gs(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var Ra=!1;function Vp(e,t){switch(e){case"compositionend":return Gs(t);case"keypress":return t.which!==32?null:(qs=!0,js);case"textInput":return e=t.data,e===js&&qs?null:e;default:return null}}function Qp(e,t){if(Ra)return e==="compositionend"||!_r&&Ys(e,t)?(e=Ns(),_u=gr=En=null,Ra=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:n,offset:t-e};e=a}e:{for(;n;){if(n.nextSibling){n=n.nextSibling;break e}n=n.parentNode}n=void 0}n=Fs(n)}}function Ws(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?Ws(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function Ps(e){e=e!=null&&e.ownerDocument!=null&&e.ownerDocument.defaultView!=null?e.ownerDocument.defaultView:window;for(var t=Su(e.document);t instanceof e.HTMLIFrameElement;){try{var n=typeof t.contentWindow.location.href=="string"}catch{n=!1}if(n)e=t.contentWindow;else break;t=Su(e.document)}return t}function Rr(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}var Ip=en&&"documentMode"in document&&11>=document.documentMode,Oa=null,Or=null,Sl=null,Cr=!1;function Is(e,t,n){var a=n.window===n?n.document:n.nodeType===9?n:n.ownerDocument;Cr||Oa==null||Oa!==Su(a)||(a=Oa,"selectionStart"in a&&Rr(a)?a={start:a.selectionStart,end:a.selectionEnd}:(a=(a.ownerDocument&&a.ownerDocument.defaultView||window).getSelection(),a={anchorNode:a.anchorNode,anchorOffset:a.anchorOffset,focusNode:a.focusNode,focusOffset:a.focusOffset}),Sl&&bl(Sl,a)||(Sl=a,a=yi(Or,"onSelect"),0>=f,u-=f,Qt=1<<32-gt(t)+u|n<re?(me=J,J=null):me=J.sibling;var ve=z(A,J,O[re],L);if(ve===null){J===null&&(J=me);break}e&&J&&ve.alternate===null&&t(A,J),_=r(ve,_,re),ge===null?$=ve:ge.sibling=ve,ge=ve,J=me}if(re===O.length)return n(A,J),ye&&nn(A,re),$;if(J===null){for(;rere?(me=J,J=null):me=J.sibling;var kn=z(A,J,ve.value,L);if(kn===null){J===null&&(J=me);break}e&&J&&kn.alternate===null&&t(A,J),_=r(kn,_,re),ge===null?$=kn:ge.sibling=kn,ge=kn,J=me}if(ve.done)return n(A,J),ye&&nn(A,re),$;if(J===null){for(;!ve.done;re++,ve=O.next())ve=H(A,ve.value,L),ve!==null&&(_=r(ve,_,re),ge===null?$=ve:ge.sibling=ve,ge=ve);return ye&&nn(A,re),$}for(J=a(J);!ve.done;re++,ve=O.next())ve=x(J,A,re,ve.value,L),ve!==null&&(e&&ve.alternate!==null&&J.delete(ve.key===null?re:ve.key),_=r(ve,_,re),ge===null?$=ve:ge.sibling=ve,ge=ve);return e&&J.forEach(function(bv){return t(A,bv)}),ye&&nn(A,re),$}function Re(A,_,O,L){if(typeof O=="object"&&O!==null&&O.type===N&&O.key===null&&(O=O.props.children),typeof O=="object"&&O!==null){switch(O.$$typeof){case Y:e:{for(var $=O.key;_!==null;){if(_.key===$){if($=O.type,$===N){if(_.tag===7){n(A,_.sibling),L=u(_,O.props.children),L.return=A,A=L;break e}}else if(_.elementType===$||typeof $=="object"&&$!==null&&$.$$typeof===Ce&&ta($)===_.type){n(A,_.sibling),L=u(_,O.props),Ol(L,O),L.return=A,A=L;break e}n(A,_);break}else t(A,_);_=_.sibling}O.type===N?(L=$n(O.props.children,A.mode,L,O.key),L.return=A,A=L):(L=Nu(O.type,O.key,O.props,null,A.mode,L),Ol(L,O),L.return=A,A=L)}return f(A);case q:e:{for($=O.key;_!==null;){if(_.key===$)if(_.tag===4&&_.stateNode.containerInfo===O.containerInfo&&_.stateNode.implementation===O.implementation){n(A,_.sibling),L=u(_,O.children||[]),L.return=A,A=L;break e}else{n(A,_);break}else t(A,_);_=_.sibling}L=Br(O,A.mode,L),L.return=A,A=L}return f(A);case Ce:return O=ta(O),Re(A,_,O,L)}if(xe(O))return Z(A,_,O,L);if(He(O)){if($=He(O),typeof $!="function")throw Error(o(150));return O=$.call(O),ee(A,_,O,L)}if(typeof O.then=="function")return Re(A,_,qu(O),L);if(O.$$typeof===X)return Re(A,_,wu(A,O),L);Yu(A,O)}return typeof O=="string"&&O!==""||typeof O=="number"||typeof O=="bigint"?(O=""+O,_!==null&&_.tag===6?(n(A,_.sibling),L=u(_,O),L.return=A,A=L):(n(A,_),L=Ur(O,A.mode,L),L.return=A,A=L),f(A)):n(A,_)}return function(A,_,O,L){try{Rl=0;var $=Re(A,_,O,L);return Ha=null,$}catch(J){if(J===La||J===Hu)throw J;var ge=bt(29,J,null,A.mode);return ge.lanes=L,ge.return=A,ge}finally{}}}var aa=Af(!0),Tf=Af(!1),On=!1;function Zr(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function Kr(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,callbacks:null})}function Cn(e){return{lane:e,tag:0,payload:null,callback:null,next:null}}function zn(e,t,n){var a=e.updateQueue;if(a===null)return null;if(a=a.shared,(be&2)!==0){var u=a.pending;return u===null?t.next=t:(t.next=u.next,u.next=t),a.pending=t,t=Mu(e),rf(e,null,n),t}return xu(e,a,t,n),Mu(e)}function Cl(e,t,n){if(t=t.updateQueue,t!==null&&(t=t.shared,(n&4194048)!==0)){var a=t.lanes;a&=e.pendingLanes,n|=a,t.lanes=n,ms(e,n)}}function Jr(e,t){var n=e.updateQueue,a=e.alternate;if(a!==null&&(a=a.updateQueue,n===a)){var u=null,r=null;if(n=n.firstBaseUpdate,n!==null){do{var f={lane:n.lane,tag:n.tag,payload:n.payload,callback:null,next:null};r===null?u=r=f:r=r.next=f,n=n.next}while(n!==null);r===null?u=r=t:r=r.next=t}else u=r=t;n={baseState:a.baseState,firstBaseUpdate:u,lastBaseUpdate:r,shared:a.shared,callbacks:a.callbacks},e.updateQueue=n;return}e=n.lastBaseUpdate,e===null?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}var Fr=!1;function zl(){if(Fr){var e=wa;if(e!==null)throw e}}function Dl(e,t,n,a){Fr=!1;var u=e.updateQueue;On=!1;var r=u.firstBaseUpdate,f=u.lastBaseUpdate,m=u.shared.pending;if(m!==null){u.shared.pending=null;var b=m,C=b.next;b.next=null,f===null?r=C:f.next=C,f=b;var w=e.alternate;w!==null&&(w=w.updateQueue,m=w.lastBaseUpdate,m!==f&&(m===null?w.firstBaseUpdate=C:m.next=C,w.lastBaseUpdate=b))}if(r!==null){var H=u.baseState;f=0,w=C=b=null,m=r;do{var z=m.lane&-536870913,x=z!==m.lane;if(x?(he&z)===z:(a&z)===z){z!==0&&z===Ba&&(Fr=!0),w!==null&&(w=w.next={lane:0,tag:m.tag,payload:m.payload,callback:null,next:null});e:{var Z=e,ee=m;z=t;var Re=n;switch(ee.tag){case 1:if(Z=ee.payload,typeof Z=="function"){H=Z.call(Re,H,z);break e}H=Z;break e;case 3:Z.flags=Z.flags&-65537|128;case 0:if(Z=ee.payload,z=typeof Z=="function"?Z.call(Re,H,z):Z,z==null)break e;H=S({},H,z);break e;case 2:On=!0}}z=m.callback,z!==null&&(e.flags|=64,x&&(e.flags|=8192),x=u.callbacks,x===null?u.callbacks=[z]:x.push(z))}else x={lane:z,tag:m.tag,payload:m.payload,callback:m.callback,next:null},w===null?(C=w=x,b=H):w=w.next=x,f|=z;if(m=m.next,m===null){if(m=u.shared.pending,m===null)break;x=m,m=x.next,x.next=null,u.lastBaseUpdate=x,u.shared.pending=null}}while(!0);w===null&&(b=H),u.baseState=b,u.firstBaseUpdate=C,u.lastBaseUpdate=w,r===null&&(u.shared.lanes=0),Un|=f,e.lanes=f,e.memoizedState=H}}function Rf(e,t){if(typeof e!="function")throw Error(o(191,e));e.call(t)}function Of(e,t){var n=e.callbacks;if(n!==null)for(e.callbacks=null,e=0;er?r:8;var f=B.T,m={};B.T=m,yo(e,!1,t,n);try{var b=u(),C=B.S;if(C!==null&&C(m,b),b!==null&&typeof b=="object"&&typeof b.then=="function"){var w=og(b,a);Nl(e,t,w,Tt(e))}else Nl(e,t,a,Tt(e))}catch(H){Nl(e,t,{then:function(){},status:"rejected",reason:H},Tt())}finally{k.p=r,f!==null&&m.types!==null&&(f.types=m.types),B.T=f}}function mg(){}function ho(e,t,n,a){if(e.tag!==5)throw Error(o(476));var u=ld(e).queue;ad(e,u,t,I,n===null?mg:function(){return ud(e),n(a)})}function ld(e){var t=e.memoizedState;if(t!==null)return t;t={memoizedState:I,baseState:I,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:rn,lastRenderedState:I},next:null};var n={};return t.next={memoizedState:n,baseState:n,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:rn,lastRenderedState:n},next:null},e.memoizedState=t,e=e.alternate,e!==null&&(e.memoizedState=t),t}function ud(e){var t=ld(e);t.next===null&&(t=e.alternate.memoizedState),Nl(e,t.next.queue,{},Tt())}function mo(){return Pe(Fl)}function id(){return qe().memoizedState}function rd(){return qe().memoizedState}function yg(e){for(var t=e.return;t!==null;){switch(t.tag){case 24:case 3:var n=Tt();e=Cn(n);var a=zn(t,e,n);a!==null&&(ft(a,t,n),Cl(a,t,n)),t={cache:Xr()},e.payload=t;return}t=t.return}}function pg(e,t,n){var a=Tt();n={lane:a,revertLane:0,gesture:null,action:n,hasEagerState:!1,eagerState:null,next:null},$u(e)?cd(t,n):(n=Mr(e,t,n,a),n!==null&&(ft(n,e,a),sd(n,t,a)))}function od(e,t,n){var a=Tt();Nl(e,t,n,a)}function Nl(e,t,n,a){var u={lane:a,revertLane:0,gesture:null,action:n,hasEagerState:!1,eagerState:null,next:null};if($u(e))cd(t,u);else{var r=e.alternate;if(e.lanes===0&&(r===null||r.lanes===0)&&(r=t.lastRenderedReducer,r!==null))try{var f=t.lastRenderedState,m=r(f,n);if(u.hasEagerState=!0,u.eagerState=m,vt(m,f))return xu(e,t,u,0),Oe===null&&Du(),!1}catch{}finally{}if(n=Mr(e,t,u,a),n!==null)return ft(n,e,a),sd(n,t,a),!0}return!1}function yo(e,t,n,a){if(a={lane:2,revertLane:Ko(),gesture:null,action:a,hasEagerState:!1,eagerState:null,next:null},$u(e)){if(t)throw Error(o(479))}else t=Mr(e,n,a,2),t!==null&&ft(t,e,2)}function $u(e){var t=e.alternate;return e===ue||t!==null&&t===ue}function cd(e,t){qa=ku=!0;var n=e.pending;n===null?t.next=t:(t.next=n.next,n.next=t),e.pending=t}function sd(e,t,n){if((n&4194048)!==0){var a=t.lanes;a&=e.pendingLanes,n|=a,t.lanes=n,ms(e,n)}}var Ul={readContext:Pe,use:Zu,useCallback:Be,useContext:Be,useEffect:Be,useImperativeHandle:Be,useLayoutEffect:Be,useInsertionEffect:Be,useMemo:Be,useReducer:Be,useRef:Be,useState:Be,useDebugValue:Be,useDeferredValue:Be,useTransition:Be,useSyncExternalStore:Be,useId:Be,useHostTransitionStatus:Be,useFormState:Be,useActionState:Be,useOptimistic:Be,useMemoCache:Be,useCacheRefresh:Be};Ul.useEffectEvent=Be;var fd={readContext:Pe,use:Zu,useCallback:function(e,t){return at().memoizedState=[e,t===void 0?null:t],e},useContext:Pe,useEffect:Jf,useImperativeHandle:function(e,t,n){n=n!=null?n.concat([e]):null,Ju(4194308,4,Pf.bind(null,t,e),n)},useLayoutEffect:function(e,t){return Ju(4194308,4,e,t)},useInsertionEffect:function(e,t){Ju(4,2,e,t)},useMemo:function(e,t){var n=at();t=t===void 0?null:t;var a=e();if(la){bn(!0);try{e()}finally{bn(!1)}}return n.memoizedState=[a,t],a},useReducer:function(e,t,n){var a=at();if(n!==void 0){var u=n(t);if(la){bn(!0);try{n(t)}finally{bn(!1)}}}else u=t;return a.memoizedState=a.baseState=u,e={pending:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:u},a.queue=e,e=e.dispatch=pg.bind(null,ue,e),[a.memoizedState,e]},useRef:function(e){var t=at();return e={current:e},t.memoizedState=e},useState:function(e){e=ro(e);var t=e.queue,n=od.bind(null,ue,t);return t.dispatch=n,[e.memoizedState,n]},useDebugValue:so,useDeferredValue:function(e,t){var n=at();return fo(n,e,t)},useTransition:function(){var e=ro(!1);return e=ad.bind(null,ue,e.queue,!0,!1),at().memoizedState=e,[!1,e]},useSyncExternalStore:function(e,t,n){var a=ue,u=at();if(ye){if(n===void 0)throw Error(o(407));n=n()}else{if(n=t(),Oe===null)throw Error(o(349));(he&127)!==0||Nf(a,t,n)}u.memoizedState=n;var r={value:n,getSnapshot:t};return u.queue=r,Jf(Bf.bind(null,a,r,e),[e]),a.flags|=2048,Ga(9,{destroy:void 0},Uf.bind(null,a,r,n,t),null),n},useId:function(){var e=at(),t=Oe.identifierPrefix;if(ye){var n=Zt,a=Qt;n=(a&~(1<<32-gt(a)-1)).toString(32)+n,t="_"+t+"R_"+n,n=Vu++,0<\/script>",r=r.removeChild(r.firstChild);break;case"select":r=typeof a.is=="string"?f.createElement("select",{is:a.is}):f.createElement("select"),a.multiple?r.multiple=!0:a.size&&(r.size=a.size);break;default:r=typeof a.is=="string"?f.createElement(u,{is:a.is}):f.createElement(u)}}r[$e]=t,r[ut]=a;e:for(f=t.child;f!==null;){if(f.tag===5||f.tag===6)r.appendChild(f.stateNode);else if(f.tag!==4&&f.tag!==27&&f.child!==null){f.child.return=f,f=f.child;continue}if(f===t)break e;for(;f.sibling===null;){if(f.return===null||f.return===t)break e;f=f.return}f.sibling.return=f.return,f=f.sibling}t.stateNode=r;e:switch(et(r,u,a),u){case"button":case"input":case"select":case"textarea":a=!!a.autoFocus;break e;case"img":a=!0;break e;default:a=!1}a&&cn(t)}}return De(t),Do(t,t.type,e===null?null:e.memoizedProps,t.pendingProps,n),null;case 6:if(e&&t.stateNode!=null)e.memoizedProps!==a&&cn(t);else{if(typeof a!="string"&&t.stateNode===null)throw Error(o(166));if(e=ie.current,Na(t)){if(e=t.stateNode,n=t.memoizedProps,a=null,u=We,u!==null)switch(u.tag){case 27:case 5:a=u.memoizedProps}e[$e]=t,e=!!(e.nodeValue===n||a!==null&&a.suppressHydrationWarning===!0||xh(e.nodeValue,n)),e||Tn(t,!0)}else e=pi(e).createTextNode(a),e[$e]=t,t.stateNode=e}return De(t),null;case 31:if(n=t.memoizedState,e===null||e.memoizedState!==null){if(a=Na(t),n!==null){if(e===null){if(!a)throw Error(o(318));if(e=t.memoizedState,e=e!==null?e.dehydrated:null,!e)throw Error(o(557));e[$e]=t}else Wn(),(t.flags&128)===0&&(t.memoizedState=null),t.flags|=4;De(t),e=!1}else n=jr(),e!==null&&e.memoizedState!==null&&(e.memoizedState.hydrationErrors=n),e=!0;if(!e)return t.flags&256?(Et(t),t):(Et(t),null);if((t.flags&128)!==0)throw Error(o(558))}return De(t),null;case 13:if(a=t.memoizedState,e===null||e.memoizedState!==null&&e.memoizedState.dehydrated!==null){if(u=Na(t),a!==null&&a.dehydrated!==null){if(e===null){if(!u)throw Error(o(318));if(u=t.memoizedState,u=u!==null?u.dehydrated:null,!u)throw Error(o(317));u[$e]=t}else Wn(),(t.flags&128)===0&&(t.memoizedState=null),t.flags|=4;De(t),u=!1}else u=jr(),e!==null&&e.memoizedState!==null&&(e.memoizedState.hydrationErrors=u),u=!0;if(!u)return t.flags&256?(Et(t),t):(Et(t),null)}return Et(t),(t.flags&128)!==0?(t.lanes=n,t):(n=a!==null,e=e!==null&&e.memoizedState!==null,n&&(a=t.child,u=null,a.alternate!==null&&a.alternate.memoizedState!==null&&a.alternate.memoizedState.cachePool!==null&&(u=a.alternate.memoizedState.cachePool.pool),r=null,a.memoizedState!==null&&a.memoizedState.cachePool!==null&&(r=a.memoizedState.cachePool.pool),r!==u&&(a.flags|=2048)),n!==e&&n&&(t.child.flags|=8192),ti(t,t.updateQueue),De(t),null);case 4:return _e(),e===null&&Wo(t.stateNode.containerInfo),De(t),null;case 10:return ln(t.type),De(t),null;case 19:if(j(je),a=t.memoizedState,a===null)return De(t),null;if(u=(t.flags&128)!==0,r=a.rendering,r===null)if(u)wl(a,!1);else{if(we!==0||e!==null&&(e.flags&128)!==0)for(e=t.child;e!==null;){if(r=Xu(e),r!==null){for(t.flags|=128,wl(a,!1),e=r.updateQueue,t.updateQueue=e,ti(t,e),t.subtreeFlags=0,e=n,n=t.child;n!==null;)of(n,e),n=n.sibling;return G(je,je.current&1|2),ye&&nn(t,a.treeForkCount),t.child}e=e.sibling}a.tail!==null&&yt()>ii&&(t.flags|=128,u=!0,wl(a,!1),t.lanes=4194304)}else{if(!u)if(e=Xu(r),e!==null){if(t.flags|=128,u=!0,e=e.updateQueue,t.updateQueue=e,ti(t,e),wl(a,!0),a.tail===null&&a.tailMode==="hidden"&&!r.alternate&&!ye)return De(t),null}else 2*yt()-a.renderingStartTime>ii&&n!==536870912&&(t.flags|=128,u=!0,wl(a,!1),t.lanes=4194304);a.isBackwards?(r.sibling=t.child,t.child=r):(e=a.last,e!==null?e.sibling=r:t.child=r,a.last=r)}return a.tail!==null?(e=a.tail,a.rendering=e,a.tail=e.sibling,a.renderingStartTime=yt(),e.sibling=null,n=je.current,G(je,u?n&1|2:n&1),ye&&nn(t,a.treeForkCount),e):(De(t),null);case 22:case 23:return Et(t),Wr(),a=t.memoizedState!==null,e!==null?e.memoizedState!==null!==a&&(t.flags|=8192):a&&(t.flags|=8192),a?(n&536870912)!==0&&(t.flags&128)===0&&(De(t),t.subtreeFlags&6&&(t.flags|=8192)):De(t),n=t.updateQueue,n!==null&&ti(t,n.retryQueue),n=null,e!==null&&e.memoizedState!==null&&e.memoizedState.cachePool!==null&&(n=e.memoizedState.cachePool.pool),a=null,t.memoizedState!==null&&t.memoizedState.cachePool!==null&&(a=t.memoizedState.cachePool.pool),a!==n&&(t.flags|=2048),e!==null&&j(ea),null;case 24:return n=null,e!==null&&(n=e.memoizedState.cache),t.memoizedState.cache!==n&&(t.flags|=2048),ln(Ge),De(t),null;case 25:return null;case 30:return null}throw Error(o(156,t.tag))}function Eg(e,t){switch(Lr(t),t.tag){case 1:return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return ln(Ge),_e(),e=t.flags,(e&65536)!==0&&(e&128)===0?(t.flags=e&-65537|128,t):null;case 26:case 27:case 5:return Wt(t),null;case 31:if(t.memoizedState!==null){if(Et(t),t.alternate===null)throw Error(o(340));Wn()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 13:if(Et(t),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(o(340));Wn()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return j(je),null;case 4:return _e(),null;case 10:return ln(t.type),null;case 22:case 23:return Et(t),Wr(),e!==null&&j(ea),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 24:return ln(Ge),null;case 25:return null;default:return null}}function wd(e,t){switch(Lr(t),t.tag){case 3:ln(Ge),_e();break;case 26:case 27:case 5:Wt(t);break;case 4:_e();break;case 31:t.memoizedState!==null&&Et(t);break;case 13:Et(t);break;case 19:j(je);break;case 10:ln(t.type);break;case 22:case 23:Et(t),Wr(),e!==null&&j(ea);break;case 24:ln(Ge)}}function Ll(e,t){try{var n=t.updateQueue,a=n!==null?n.lastEffect:null;if(a!==null){var u=a.next;n=u;do{if((n.tag&e)===e){a=void 0;var r=n.create,f=n.inst;a=r(),f.destroy=a}n=n.next}while(n!==u)}}catch(m){Ee(t,t.return,m)}}function Mn(e,t,n){try{var a=t.updateQueue,u=a!==null?a.lastEffect:null;if(u!==null){var r=u.next;a=r;do{if((a.tag&e)===e){var f=a.inst,m=f.destroy;if(m!==void 0){f.destroy=void 0,u=t;var b=n,C=m;try{C()}catch(w){Ee(u,b,w)}}}a=a.next}while(a!==r)}}catch(w){Ee(t,t.return,w)}}function Ld(e){var t=e.updateQueue;if(t!==null){var n=e.stateNode;try{Of(t,n)}catch(a){Ee(e,e.return,a)}}}function Hd(e,t,n){n.props=ua(e.type,e.memoizedProps),n.state=e.memoizedState;try{n.componentWillUnmount()}catch(a){Ee(e,t,a)}}function Hl(e,t){try{var n=e.ref;if(n!==null){switch(e.tag){case 26:case 27:case 5:var a=e.stateNode;break;case 30:a=e.stateNode;break;default:a=e.stateNode}typeof n=="function"?e.refCleanup=n(a):n.current=a}}catch(u){Ee(e,t,u)}}function Kt(e,t){var n=e.ref,a=e.refCleanup;if(n!==null)if(typeof a=="function")try{a()}catch(u){Ee(e,t,u)}finally{e.refCleanup=null,e=e.alternate,e!=null&&(e.refCleanup=null)}else if(typeof n=="function")try{n(null)}catch(u){Ee(e,t,u)}else n.current=null}function jd(e){var t=e.type,n=e.memoizedProps,a=e.stateNode;try{e:switch(t){case"button":case"input":case"select":case"textarea":n.autoFocus&&a.focus();break e;case"img":n.src?a.src=n.src:n.srcSet&&(a.srcset=n.srcSet)}}catch(u){Ee(e,e.return,u)}}function xo(e,t,n){try{var a=e.stateNode;kg(a,e.type,n,t),a[ut]=t}catch(u){Ee(e,e.return,u)}}function qd(e){return e.tag===5||e.tag===3||e.tag===26||e.tag===27&&jn(e.type)||e.tag===4}function Mo(e){e:for(;;){for(;e.sibling===null;){if(e.return===null||qd(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.tag===27&&jn(e.type)||e.flags&2||e.child===null||e.tag===4)continue e;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function No(e,t,n){var a=e.tag;if(a===5||a===6)e=e.stateNode,t?(n.nodeType===9?n.body:n.nodeName==="HTML"?n.ownerDocument.body:n).insertBefore(e,t):(t=n.nodeType===9?n.body:n.nodeName==="HTML"?n.ownerDocument.body:n,t.appendChild(e),n=n._reactRootContainer,n!=null||t.onclick!==null||(t.onclick=It));else if(a!==4&&(a===27&&jn(e.type)&&(n=e.stateNode,t=null),e=e.child,e!==null))for(No(e,t,n),e=e.sibling;e!==null;)No(e,t,n),e=e.sibling}function ni(e,t,n){var a=e.tag;if(a===5||a===6)e=e.stateNode,t?n.insertBefore(e,t):n.appendChild(e);else if(a!==4&&(a===27&&jn(e.type)&&(n=e.stateNode),e=e.child,e!==null))for(ni(e,t,n),e=e.sibling;e!==null;)ni(e,t,n),e=e.sibling}function Yd(e){var t=e.stateNode,n=e.memoizedProps;try{for(var a=e.type,u=t.attributes;u.length;)t.removeAttributeNode(u[0]);et(t,a,n),t[$e]=e,t[ut]=n}catch(r){Ee(e,e.return,r)}}var sn=!1,Ve=!1,Uo=!1,Gd=typeof WeakSet=="function"?WeakSet:Set,Fe=null;function _g(e,t){if(e=e.containerInfo,ec=Ai,e=Ps(e),Rr(e)){if("selectionStart"in e)var n={start:e.selectionStart,end:e.selectionEnd};else e:{n=(n=e.ownerDocument)&&n.defaultView||window;var a=n.getSelection&&n.getSelection();if(a&&a.rangeCount!==0){n=a.anchorNode;var u=a.anchorOffset,r=a.focusNode;a=a.focusOffset;try{n.nodeType,r.nodeType}catch{n=null;break e}var f=0,m=-1,b=-1,C=0,w=0,H=e,z=null;t:for(;;){for(var x;H!==n||u!==0&&H.nodeType!==3||(m=f+u),H!==r||a!==0&&H.nodeType!==3||(b=f+a),H.nodeType===3&&(f+=H.nodeValue.length),(x=H.firstChild)!==null;)z=H,H=x;for(;;){if(H===e)break t;if(z===n&&++C===u&&(m=f),z===r&&++w===a&&(b=f),(x=H.nextSibling)!==null)break;H=z,z=H.parentNode}H=x}n=m===-1||b===-1?null:{start:m,end:b}}else n=null}n=n||{start:0,end:0}}else n=null;for(tc={focusedElem:e,selectionRange:n},Ai=!1,Fe=t;Fe!==null;)if(t=Fe,e=t.child,(t.subtreeFlags&1028)!==0&&e!==null)e.return=t,Fe=e;else for(;Fe!==null;){switch(t=Fe,r=t.alternate,e=t.flags,t.tag){case 0:if((e&4)!==0&&(e=t.updateQueue,e=e!==null?e.events:null,e!==null))for(n=0;n title"))),et(r,a,n),r[$e]=e,Je(r),a=r;break e;case"link":var f=Kh("link","href",u).get(a+(n.href||""));if(f){for(var m=0;mRe&&(f=Re,Re=ee,ee=f);var A=$s(m,ee),_=$s(m,Re);if(A&&_&&(x.rangeCount!==1||x.anchorNode!==A.node||x.anchorOffset!==A.offset||x.focusNode!==_.node||x.focusOffset!==_.offset)){var O=H.createRange();O.setStart(A.node,A.offset),x.removeAllRanges(),ee>Re?(x.addRange(O),x.extend(_.node,_.offset)):(O.setEnd(_.node,_.offset),x.addRange(O))}}}}for(H=[],x=m;x=x.parentNode;)x.nodeType===1&&H.push({element:x,left:x.scrollLeft,top:x.scrollTop});for(typeof m.focus=="function"&&m.focus(),m=0;mn?32:n,B.T=null,n=Yo,Yo=null;var r=wn,f=yn;if(Ke=0,Za=wn=null,yn=0,(be&6)!==0)throw Error(o(331));var m=be;if(be|=4,Pd(r.current),Fd(r,r.current,f,n),be=m,kl(0,!1),pt&&typeof pt.onPostCommitFiberRoot=="function")try{pt.onPostCommitFiberRoot(rl,r)}catch{}return!0}finally{k.p=u,B.T=a,ph(e,t)}}function vh(e,t,n){t=Dt(n,t),t=bo(e.stateNode,t,2),e=zn(e,t,2),e!==null&&(cl(e,2),Jt(e))}function Ee(e,t,n){if(e.tag===3)vh(e,e,n);else for(;t!==null;){if(t.tag===3){vh(t,e,n);break}else if(t.tag===1){var a=t.stateNode;if(typeof t.type.getDerivedStateFromError=="function"||typeof a.componentDidCatch=="function"&&(Bn===null||!Bn.has(a))){e=Dt(n,e),n=bd(2),a=zn(t,n,2),a!==null&&(Sd(n,a,t,e),cl(a,2),Jt(a));break}}t=t.return}}function Vo(e,t,n){var a=e.pingCache;if(a===null){a=e.pingCache=new Rg;var u=new Set;a.set(t,u)}else u=a.get(t),u===void 0&&(u=new Set,a.set(t,u));u.has(n)||(Lo=!0,u.add(n),e=xg.bind(null,e,t,n),t.then(e,e))}function xg(e,t,n){var a=e.pingCache;a!==null&&a.delete(t),e.pingedLanes|=e.suspendedLanes&n,e.warmLanes&=~n,Oe===e&&(he&n)===n&&(we===4||we===3&&(he&62914560)===he&&300>yt()-ui?(be&2)===0&&Ka(e,0):Ho|=n,Qa===he&&(Qa=0)),Jt(e)}function bh(e,t){t===0&&(t=ds()),e=Fn(e,t),e!==null&&(cl(e,t),Jt(e))}function Mg(e){var t=e.memoizedState,n=0;t!==null&&(n=t.retryLane),bh(e,n)}function Ng(e,t){var n=0;switch(e.tag){case 31:case 13:var a=e.stateNode,u=e.memoizedState;u!==null&&(n=u.retryLane);break;case 19:a=e.stateNode;break;case 22:a=e.stateNode._retryCache;break;default:throw Error(o(314))}a!==null&&a.delete(t),bh(e,n)}function Ug(e,t){return nr(e,t)}var di=null,Fa=null,Qo=!1,hi=!1,Zo=!1,Hn=0;function Jt(e){e!==Fa&&e.next===null&&(Fa===null?di=Fa=e:Fa=Fa.next=e),hi=!0,Qo||(Qo=!0,wg())}function kl(e,t){if(!Zo&&hi){Zo=!0;do for(var n=!1,a=di;a!==null;){if(e!==0){var u=a.pendingLanes;if(u===0)var r=0;else{var f=a.suspendedLanes,m=a.pingedLanes;r=(1<<31-gt(42|e)+1)-1,r&=u&~(f&~m),r=r&201326741?r&201326741|1:r?r|2:0}r!==0&&(n=!0,Ah(a,r))}else r=he,r=gu(a,a===Oe?r:0,a.cancelPendingCommit!==null||a.timeoutHandle!==-1),(r&3)===0||ol(a,r)||(n=!0,Ah(a,r));a=a.next}while(n);Zo=!1}}function Bg(){Sh()}function Sh(){hi=Qo=!1;var e=0;Hn!==0&&Qg()&&(e=Hn);for(var t=yt(),n=null,a=di;a!==null;){var u=a.next,r=Eh(a,t);r===0?(a.next=null,n===null?di=u:n.next=u,u===null&&(Fa=n)):(n=a,(e!==0||(r&3)!==0)&&(hi=!0)),a=u}Ke!==0&&Ke!==5||kl(e),Hn!==0&&(Hn=0)}function Eh(e,t){for(var n=e.suspendedLanes,a=e.pingedLanes,u=e.expirationTimes,r=e.pendingLanes&-62914561;0m)break;var w=b.transferSize,H=b.initiatorType;w&&Mh(H)&&(b=b.responseEnd,f+=w*(b"u"?null:document;function kh(e,t,n){var a=$a;if(a&&typeof t=="string"&&t){var u=Ct(t);u='link[rel="'+e+'"][href="'+u+'"]',typeof n=="string"&&(u+='[crossorigin="'+n+'"]'),Xh.has(u)||(Xh.add(u),e={rel:e,crossOrigin:n,href:t},a.querySelector(u)===null&&(t=a.createElement("link"),et(t,"link",e),Je(t),a.head.appendChild(t)))}}function ev(e){pn.D(e),kh("dns-prefetch",e,null)}function tv(e,t){pn.C(e,t),kh("preconnect",e,t)}function nv(e,t,n){pn.L(e,t,n);var a=$a;if(a&&e&&t){var u='link[rel="preload"][as="'+Ct(t)+'"]';t==="image"&&n&&n.imageSrcSet?(u+='[imagesrcset="'+Ct(n.imageSrcSet)+'"]',typeof n.imageSizes=="string"&&(u+='[imagesizes="'+Ct(n.imageSizes)+'"]')):u+='[href="'+Ct(e)+'"]';var r=u;switch(t){case"style":r=Wa(e);break;case"script":r=Pa(e)}wt.has(r)||(e=S({rel:"preload",href:t==="image"&&n&&n.imageSrcSet?void 0:e,as:t},n),wt.set(r,e),a.querySelector(u)!==null||t==="style"&&a.querySelector(Kl(r))||t==="script"&&a.querySelector(Jl(r))||(t=a.createElement("link"),et(t,"link",e),Je(t),a.head.appendChild(t)))}}function av(e,t){pn.m(e,t);var n=$a;if(n&&e){var a=t&&typeof t.as=="string"?t.as:"script",u='link[rel="modulepreload"][as="'+Ct(a)+'"][href="'+Ct(e)+'"]',r=u;switch(a){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":r=Pa(e)}if(!wt.has(r)&&(e=S({rel:"modulepreload",href:e},t),wt.set(r,e),n.querySelector(u)===null)){switch(a){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":if(n.querySelector(Jl(r)))return}a=n.createElement("link"),et(a,"link",e),Je(a),n.head.appendChild(a)}}}function lv(e,t,n){pn.S(e,t,n);var a=$a;if(a&&e){var u=ba(a).hoistableStyles,r=Wa(e);t=t||"default";var f=u.get(r);if(!f){var m={loading:0,preload:null};if(f=a.querySelector(Kl(r)))m.loading=5;else{e=S({rel:"stylesheet",href:e,"data-precedence":t},n),(n=wt.get(r))&&oc(e,n);var b=f=a.createElement("link");Je(b),et(b,"link",e),b._p=new Promise(function(C,w){b.onload=C,b.onerror=w}),b.addEventListener("load",function(){m.loading|=1}),b.addEventListener("error",function(){m.loading|=2}),m.loading|=4,vi(f,t,a)}f={type:"stylesheet",instance:f,count:1,state:m},u.set(r,f)}}}function uv(e,t){pn.X(e,t);var n=$a;if(n&&e){var a=ba(n).hoistableScripts,u=Pa(e),r=a.get(u);r||(r=n.querySelector(Jl(u)),r||(e=S({src:e,async:!0},t),(t=wt.get(u))&&cc(e,t),r=n.createElement("script"),Je(r),et(r,"link",e),n.head.appendChild(r)),r={type:"script",instance:r,count:1,state:null},a.set(u,r))}}function iv(e,t){pn.M(e,t);var n=$a;if(n&&e){var a=ba(n).hoistableScripts,u=Pa(e),r=a.get(u);r||(r=n.querySelector(Jl(u)),r||(e=S({src:e,async:!0,type:"module"},t),(t=wt.get(u))&&cc(e,t),r=n.createElement("script"),Je(r),et(r,"link",e),n.head.appendChild(r)),r={type:"script",instance:r,count:1,state:null},a.set(u,r))}}function Vh(e,t,n,a){var u=(u=ie.current)?gi(u):null;if(!u)throw Error(o(446));switch(e){case"meta":case"title":return null;case"style":return typeof n.precedence=="string"&&typeof n.href=="string"?(t=Wa(n.href),n=ba(u).hoistableStyles,a=n.get(t),a||(a={type:"style",instance:null,count:0,state:null},n.set(t,a)),a):{type:"void",instance:null,count:0,state:null};case"link":if(n.rel==="stylesheet"&&typeof n.href=="string"&&typeof n.precedence=="string"){e=Wa(n.href);var r=ba(u).hoistableStyles,f=r.get(e);if(f||(u=u.ownerDocument||u,f={type:"stylesheet",instance:null,count:0,state:{loading:0,preload:null}},r.set(e,f),(r=u.querySelector(Kl(e)))&&!r._p&&(f.instance=r,f.state.loading=5),wt.has(e)||(n={rel:"preload",as:"style",href:n.href,crossOrigin:n.crossOrigin,integrity:n.integrity,media:n.media,hrefLang:n.hrefLang,referrerPolicy:n.referrerPolicy},wt.set(e,n),r||rv(u,e,n,f.state))),t&&a===null)throw Error(o(528,""));return f}if(t&&a!==null)throw Error(o(529,""));return null;case"script":return t=n.async,n=n.src,typeof n=="string"&&t&&typeof t!="function"&&typeof t!="symbol"?(t=Pa(n),n=ba(u).hoistableScripts,a=n.get(t),a||(a={type:"script",instance:null,count:0,state:null},n.set(t,a)),a):{type:"void",instance:null,count:0,state:null};default:throw Error(o(444,e))}}function Wa(e){return'href="'+Ct(e)+'"'}function Kl(e){return'link[rel="stylesheet"]['+e+"]"}function Qh(e){return S({},e,{"data-precedence":e.precedence,precedence:null})}function rv(e,t,n,a){e.querySelector('link[rel="preload"][as="style"]['+t+"]")?a.loading=1:(t=e.createElement("link"),a.preload=t,t.addEventListener("load",function(){return a.loading|=1}),t.addEventListener("error",function(){return a.loading|=2}),et(t,"link",n),Je(t),e.head.appendChild(t))}function Pa(e){return'[src="'+Ct(e)+'"]'}function Jl(e){return"script[async]"+e}function Zh(e,t,n){if(t.count++,t.instance===null)switch(t.type){case"style":var a=e.querySelector('style[data-href~="'+Ct(n.href)+'"]');if(a)return t.instance=a,Je(a),a;var u=S({},n,{"data-href":n.href,"data-precedence":n.precedence,href:null,precedence:null});return a=(e.ownerDocument||e).createElement("style"),Je(a),et(a,"style",u),vi(a,n.precedence,e),t.instance=a;case"stylesheet":u=Wa(n.href);var r=e.querySelector(Kl(u));if(r)return t.state.loading|=4,t.instance=r,Je(r),r;a=Qh(n),(u=wt.get(u))&&oc(a,u),r=(e.ownerDocument||e).createElement("link"),Je(r);var f=r;return f._p=new Promise(function(m,b){f.onload=m,f.onerror=b}),et(r,"link",a),t.state.loading|=4,vi(r,n.precedence,e),t.instance=r;case"script":return r=Pa(n.src),(u=e.querySelector(Jl(r)))?(t.instance=u,Je(u),u):(a=n,(u=wt.get(r))&&(a=S({},n),cc(a,u)),e=e.ownerDocument||e,u=e.createElement("script"),Je(u),et(u,"link",a),e.head.appendChild(u),t.instance=u);case"void":return null;default:throw Error(o(443,t.type))}else t.type==="stylesheet"&&(t.state.loading&4)===0&&(a=t.instance,t.state.loading|=4,vi(a,n.precedence,e));return t.instance}function vi(e,t,n){for(var a=n.querySelectorAll('link[rel="stylesheet"][data-precedence],style[data-precedence]'),u=a.length?a[a.length-1]:null,r=u,f=0;f title"):null)}function ov(e,t,n){if(n===1||t.itemProp!=null)return!1;switch(e){case"meta":case"title":return!0;case"style":if(typeof t.precedence!="string"||typeof t.href!="string"||t.href==="")break;return!0;case"link":if(typeof t.rel!="string"||typeof t.href!="string"||t.href===""||t.onLoad||t.onError)break;switch(t.rel){case"stylesheet":return e=t.disabled,typeof t.precedence=="string"&&e==null;default:return!0}case"script":if(t.async&&typeof t.async!="function"&&typeof t.async!="symbol"&&!t.onLoad&&!t.onError&&t.src&&typeof t.src=="string")return!0}return!1}function Fh(e){return!(e.type==="stylesheet"&&(e.state.loading&3)===0)}function cv(e,t,n,a){if(n.type==="stylesheet"&&(typeof a.media!="string"||matchMedia(a.media).matches!==!1)&&(n.state.loading&4)===0){if(n.instance===null){var u=Wa(a.href),r=t.querySelector(Kl(u));if(r){t=r._p,t!==null&&typeof t=="object"&&typeof t.then=="function"&&(e.count++,e=Si.bind(e),t.then(e,e)),n.state.loading|=4,n.instance=r,Je(r);return}r=t.ownerDocument||t,a=Qh(a),(u=wt.get(u))&&oc(a,u),r=r.createElement("link"),Je(r);var f=r;f._p=new Promise(function(m,b){f.onload=m,f.onerror=b}),et(r,"link",a),n.instance=r}e.stylesheets===null&&(e.stylesheets=new Map),e.stylesheets.set(n,t),(t=n.state.preload)&&(n.state.loading&3)===0&&(e.count++,n=Si.bind(e),t.addEventListener("load",n),t.addEventListener("error",n))}}var sc=0;function sv(e,t){return e.stylesheets&&e.count===0&&_i(e,e.stylesheets),0sc?50:800)+t);return e.unsuspend=n,function(){e.unsuspend=null,clearTimeout(a),clearTimeout(u)}}:null}function Si(){if(this.count--,this.count===0&&(this.imgCount===0||!this.waitingForImages)){if(this.stylesheets)_i(this,this.stylesheets);else if(this.unsuspend){var e=this.unsuspend;this.unsuspend=null,e()}}}var Ei=null;function _i(e,t){e.stylesheets=null,e.unsuspend!==null&&(e.count++,Ei=new Map,t.forEach(fv,e),Ei=null,Si.call(e))}function fv(e,t){if(!(t.state.loading&4)){var n=Ei.get(e);if(n)var a=n.get(null);else{n=new Map,Ei.set(e,n);for(var u=e.querySelectorAll("link[data-precedence],style[data-precedence]"),r=0;r"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(l)}catch(i){console.error(i)}}return l(),bc.exports=zv(),bc.exports}var xv=Dv();const Mv=Fm(xv),Nv="modulepreload",Uv=function(l){return"/"+l},bm={},ma=function(i,c,o){let s=Promise.resolve();if(c&&c.length>0){let h=function(y){return Promise.all(y.map(v=>Promise.resolve(v).then(S=>({status:"fulfilled",value:S}),S=>({status:"rejected",reason:S}))))};document.getElementsByTagName("link");const p=document.querySelector("meta[property=csp-nonce]"),g=(p==null?void 0:p.nonce)||(p==null?void 0:p.getAttribute("nonce"));s=h(c.map(y=>{if(y=Uv(y),y in bm)return;bm[y]=!0;const v=y.endsWith(".css"),S=v?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${y}"]${S}`))return;const M=document.createElement("link");if(M.rel=v?"stylesheet":Nv,v||(M.as="script"),M.crossOrigin="",M.href=y,g&&M.setAttribute("nonce",g),document.head.appendChild(M),v)return new Promise((Y,q)=>{M.addEventListener("load",Y),M.addEventListener("error",()=>q(new Error(`Unable to preload CSS for ${y}`)))})}))}function d(h){const p=new Event("vite:preloadError",{cancelable:!0});if(p.payload=h,window.dispatchEvent(p),!p.defaultPrevented)throw h}return s.then(h=>{for(const p of h||[])p.status==="rejected"&&d(p.reason);return i().catch(d)})};/** + * react-router v7.16.0 + * + * Copyright (c) Remix Software Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE.md file in the root directory of this source tree. + * + * @license MIT + */var Sm="popstate";function Em(l){return typeof l=="object"&&l!=null&&"pathname"in l&&"search"in l&&"hash"in l&&"state"in l&&"key"in l}function Bv(l={}){function i(o,s){var y;let d=(y=s.state)==null?void 0:y.masked,{pathname:h,search:p,hash:g}=d||o.location;return Bc("",{pathname:h,search:p,hash:g},s.state&&s.state.usr||null,s.state&&s.state.key||"default",d?{pathname:o.location.pathname,search:o.location.search,hash:o.location.hash}:void 0)}function c(o,s){return typeof s=="string"?s:lu(s)}return Lv(i,c,null,l)}function Ne(l,i){if(l===!1||l===null||typeof l>"u")throw new Error(i)}function Gt(l,i){if(!l){typeof console<"u"&&console.warn(i);try{throw new Error(i)}catch{}}}function wv(){return Math.random().toString(36).substring(2,10)}function _m(l,i){return{usr:l.state,key:l.key,idx:i,masked:l.mask?{pathname:l.pathname,search:l.search,hash:l.hash}:void 0}}function Bc(l,i,c=null,o,s){return{pathname:typeof l=="string"?l:l.pathname,search:"",hash:"",...typeof i=="string"?al(i):i,state:c,key:i&&i.key||o||wv(),mask:s}}function lu({pathname:l="/",search:i="",hash:c=""}){return i&&i!=="?"&&(l+=i.charAt(0)==="?"?i:"?"+i),c&&c!=="#"&&(l+=c.charAt(0)==="#"?c:"#"+c),l}function al(l){let i={};if(l){let c=l.indexOf("#");c>=0&&(i.hash=l.substring(c),l=l.substring(0,c));let o=l.indexOf("?");o>=0&&(i.search=l.substring(o),l=l.substring(0,o)),l&&(i.pathname=l)}return i}function Lv(l,i,c,o={}){let{window:s=document.defaultView,v5Compat:d=!1}=o,h=s.history,p="POP",g=null,y=v();y==null&&(y=0,h.replaceState({...h.state,idx:y},""));function v(){return(h.state||{idx:null}).idx}function S(){p="POP";let D=v(),U=D==null?null:D-y;y=D,g&&g({action:p,location:N.location,delta:U})}function M(D,U){p="PUSH";let Q=Em(D)?D:Bc(N.location,D,U);y=v()+1;let X=_m(Q,y),F=N.createHref(Q.mask||Q);try{h.pushState(X,"",F)}catch(ae){if(ae instanceof DOMException&&ae.name==="DataCloneError")throw ae;s.location.assign(F)}d&&g&&g({action:p,location:N.location,delta:1})}function Y(D,U){p="REPLACE";let Q=Em(D)?D:Bc(N.location,D,U);y=v();let X=_m(Q,y),F=N.createHref(Q.mask||Q);h.replaceState(X,"",F),d&&g&&g({action:p,location:N.location,delta:0})}function q(D){return Hv(s,D)}let N={get action(){return p},get location(){return l(s,h)},listen(D){if(g)throw new Error("A history only accepts one active listener");return s.addEventListener(Sm,S),g=D,()=>{s.removeEventListener(Sm,S),g=null}},createHref(D){return i(s,D)},createURL:q,encodeLocation(D){let U=q(D);return{pathname:U.pathname,search:U.search,hash:U.hash}},push:M,replace:Y,go(D){return h.go(D)}};return N}function Hv(l,i,c=!1){let o="http://localhost";l&&(o=l.location.origin!=="null"?l.location.origin:l.location.href),Ne(o,"No window.location.(origin|href) available to create URL");let s=typeof i=="string"?i:lu(i);return s=s.replace(/ $/,"%20"),!c&&s.startsWith("//")&&(s=o+s),new URL(s,o)}function $m(l,i,c="/"){return jv(l,i,c,!1)}function jv(l,i,c,o,s){let d=typeof i=="string"?al(i):i,h=vn(d.pathname||"/",c);if(h==null)return null;let p=qv(l),g=null,y=Wv(h);for(let v=0;g==null&&v{let v={relativePath:y===void 0?h.path||"":y,caseSensitive:h.caseSensitive===!0,childrenIndex:p,route:h};if(v.relativePath.startsWith("/")){if(!v.relativePath.startsWith(o)&&g)return;Ne(v.relativePath.startsWith(o),`Absolute route path "${v.relativePath}" nested under path "${o}" is not valid. An absolute child route path must start with the combined path of all its parent routes.`),v.relativePath=v.relativePath.slice(o.length)}let S=Yt([o,v.relativePath]),M=c.concat(v);h.children&&h.children.length>0&&(Ne(h.index!==!0,`Index routes must not have child routes. Please remove all child routes from route path "${S}".`),Wm(h.children,i,M,S,g)),!(h.path==null&&!h.index)&&i.push({path:S,score:Kv(S,h.index),routesMeta:M})};return l.forEach((h,p)=>{var g;if(h.path===""||!((g=h.path)!=null&&g.includes("?")))d(h,p);else for(let y of Pm(h.path))d(h,p,!0,y)}),i}function Pm(l){let i=l.split("/");if(i.length===0)return[];let[c,...o]=i,s=c.endsWith("?"),d=c.replace(/\?$/,"");if(o.length===0)return s?[d,""]:[d];let h=Pm(o.join("/")),p=[];return p.push(...h.map(g=>g===""?d:[d,g].join("/"))),s&&p.push(...h),p.map(g=>l.startsWith("/")&&g===""?"/":g)}function Yv(l){l.sort((i,c)=>i.score!==c.score?c.score-i.score:Jv(i.routesMeta.map(o=>o.childrenIndex),c.routesMeta.map(o=>o.childrenIndex)))}var Gv=/^:[\w-]+$/,Xv=3,kv=2,Vv=1,Qv=10,Zv=-2,Am=l=>l==="*";function Kv(l,i){let c=l.split("/"),o=c.length;return c.some(Am)&&(o+=Zv),i&&(o+=kv),c.filter(s=>!Am(s)).reduce((s,d)=>s+(Gv.test(d)?Xv:d===""?Vv:Qv),o)}function Jv(l,i){return l.length===i.length&&l.slice(0,-1).every((o,s)=>o===i[s])?l[l.length-1]-i[i.length-1]:0}function Fv(l,i,c=!1){let{routesMeta:o}=l,s={},d="/",h=[];for(let p=0;p{if(v==="*"){let q=p[M]||"";h=d.slice(0,d.length-q.length).replace(/(.)\/+$/,"$1")}const Y=p[M];return S&&!Y?y[v]=void 0:y[v]=(Y||"").replace(/%2F/g,"/"),y},{}),pathname:d,pathnameBase:h,pattern:l}}function $v(l,i=!1,c=!0){Gt(l==="*"||!l.endsWith("*")||l.endsWith("/*"),`Route path "${l}" will be treated as if it were "${l.replace(/\*$/,"/*")}" because the \`*\` character must always follow a \`/\` in the pattern. To get rid of this warning, please change the route path to "${l.replace(/\*$/,"/*")}".`);let o=[],s="^"+l.replace(/\/*\*?$/,"").replace(/^\/*/,"/").replace(/[\\.*+^${}|()[\]]/g,"\\$&").replace(/\/:([\w-]+)(\?)?/g,(h,p,g,y,v)=>{if(o.push({paramName:p,isOptional:g!=null}),g){let S=v.charAt(y+h.length);return S&&S!=="/"?"/([^\\/]*)":"(?:/([^\\/]*))?"}return"/([^\\/]+)"}).replace(/\/([\w-]+)\?(\/|$)/g,"(/$1)?$2");return l.endsWith("*")?(o.push({paramName:"*"}),s+=l==="*"||l==="/*"?"(.*)$":"(?:\\/(.+)|\\/*)$"):c?s+="\\/*$":l!==""&&l!=="/"&&(s+="(?:(?=\\/|$))"),[new RegExp(s,i?void 0:"i"),o]}function Wv(l){try{return l.split("/").map(i=>decodeURIComponent(i).replace(/\//g,"%2F")).join("/")}catch(i){return Gt(!1,`The URL path "${l}" could not be decoded because it is a malformed URL segment. This is probably due to a bad percent encoding (${i}).`),l}}function vn(l,i){if(i==="/")return l;if(!l.toLowerCase().startsWith(i.toLowerCase()))return null;let c=i.endsWith("/")?i.length-1:i.length,o=l.charAt(c);return o&&o!=="/"?null:l.slice(c)||"/"}var Pv=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i;function Iv(l,i="/"){let{pathname:c,search:o="",hash:s=""}=typeof l=="string"?al(l):l,d;return c?(c=Im(c),c.startsWith("/")?d=Tm(c.substring(1),"/"):d=Tm(c,i)):d=i,{pathname:d,search:n0(o),hash:a0(s)}}function Tm(l,i){let c=Gi(i).split("/");return l.split("/").forEach(s=>{s===".."?c.length>1&&c.pop():s!=="."&&c.push(s)}),c.length>1?c.join("/"):"/"}function Ac(l,i,c,o){return`Cannot include a '${l}' character in a manually specified \`to.${i}\` field [${JSON.stringify(o)}]. Please separate it out to the \`to.${c}\` field. Alternatively you may provide the full path as a string in and the router will parse it for you.`}function e0(l){return l.filter((i,c)=>c===0||i.route.path&&i.route.path.length>0)}function Qc(l){let i=e0(l);return i.map((c,o)=>o===i.length-1?c.pathname:c.pathnameBase)}function Vi(l,i,c,o=!1){let s;typeof l=="string"?s=al(l):(s={...l},Ne(!s.pathname||!s.pathname.includes("?"),Ac("?","pathname","search",s)),Ne(!s.pathname||!s.pathname.includes("#"),Ac("#","pathname","hash",s)),Ne(!s.search||!s.search.includes("#"),Ac("#","search","hash",s)));let d=l===""||s.pathname==="",h=d?"/":s.pathname,p;if(h==null)p=c;else{let S=i.length-1;if(!o&&h.startsWith("..")){let M=h.split("/");for(;M[0]==="..";)M.shift(),S-=1;s.pathname=M.join("/")}p=S>=0?i[S]:"/"}let g=Iv(s,p),y=h&&h!=="/"&&h.endsWith("/"),v=(d||h===".")&&c.endsWith("/");return!g.pathname.endsWith("/")&&(y||v)&&(g.pathname+="/"),g}var Im=l=>l.replace(/\/\/+/g,"/"),Yt=l=>Im(l.join("/")),Gi=l=>l.replace(/\/+$/,""),t0=l=>Gi(l).replace(/^\/*/,"/"),n0=l=>!l||l==="?"?"":l.startsWith("?")?l:"?"+l,a0=l=>!l||l==="#"?"":l.startsWith("#")?l:"#"+l,l0=class{constructor(l,i,c,o=!1){this.status=l,this.statusText=i||"",this.internal=o,c instanceof Error?(this.data=c.toString(),this.error=c):this.data=c}};function u0(l){return l!=null&&typeof l.status=="number"&&typeof l.statusText=="string"&&typeof l.internal=="boolean"&&"data"in l}function i0(l){let i=l.map(c=>c.route.path).filter(Boolean);return Yt(i)||"/"}var ey=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u";function ty(l,i){let c=l;if(typeof c!="string"||!Pv.test(c))return{absoluteURL:void 0,isExternal:!1,to:c};let o=c,s=!1;if(ey)try{let d=new URL(window.location.href),h=c.startsWith("//")?new URL(d.protocol+c):new URL(c),p=vn(h.pathname,i);h.origin===d.origin&&p!=null?c=p+h.search+h.hash:s=!0}catch{Gt(!1,` contains an invalid URL which will probably break when clicked - please update to a valid URL path.`)}return{absoluteURL:o,isExternal:s,to:c}}Object.getOwnPropertyNames(Object.prototype).sort().join("\0");var ny=["POST","PUT","PATCH","DELETE"];new Set(ny);var r0=["GET",...ny];new Set(r0);var ll=T.createContext(null);ll.displayName="DataRouter";var Qi=T.createContext(null);Qi.displayName="DataRouterState";var ay=T.createContext(!1);function o0(){return T.useContext(ay)}var ly=T.createContext({isTransitioning:!1});ly.displayName="ViewTransition";var c0=T.createContext(new Map);c0.displayName="Fetchers";var s0=T.createContext(null);s0.displayName="Await";var Rt=T.createContext(null);Rt.displayName="Navigation";var uu=T.createContext(null);uu.displayName="Location";var Xt=T.createContext({outlet:null,matches:[],isDataRoute:!1});Xt.displayName="Route";var Zc=T.createContext(null);Zc.displayName="RouteError";var uy="REACT_ROUTER_ERROR",f0="REDIRECT",d0="ROUTE_ERROR_RESPONSE";function h0(l){if(l.startsWith(`${uy}:${f0}:{`))try{let i=JSON.parse(l.slice(28));if(typeof i=="object"&&i&&typeof i.status=="number"&&typeof i.statusText=="string"&&typeof i.location=="string"&&typeof i.reloadDocument=="boolean"&&typeof i.replace=="boolean")return i}catch{}}function m0(l){if(l.startsWith(`${uy}:${d0}:{`))try{let i=JSON.parse(l.slice(40));if(typeof i=="object"&&i&&typeof i.status=="number"&&typeof i.statusText=="string")return new l0(i.status,i.statusText,i.data)}catch{}}function y0(l,{relative:i}={}){Ne(ul(),"useHref() may be used only in the context of a component.");let{basename:c,navigator:o}=T.useContext(Rt),{hash:s,pathname:d,search:h}=iu(l,{relative:i}),p=d;return c!=="/"&&(p=d==="/"?c:Yt([c,d])),o.createHref({pathname:p,search:h,hash:s})}function ul(){return T.useContext(uu)!=null}function $t(){return Ne(ul(),"useLocation() may be used only in the context of a component."),T.useContext(uu).location}var iy="You should call navigate() in a React.useEffect(), not when your component is first rendered.";function ry(l){T.useContext(Rt).static||T.useLayoutEffect(l)}function oy(){let{isDataRoute:l}=T.useContext(Xt);return l?D0():p0()}function p0(){Ne(ul(),"useNavigate() may be used only in the context of a component.");let l=T.useContext(ll),{basename:i,navigator:c}=T.useContext(Rt),{matches:o}=T.useContext(Xt),{pathname:s}=$t(),d=JSON.stringify(Qc(o)),h=T.useRef(!1);return ry(()=>{h.current=!0}),T.useCallback((g,y={})=>{if(Gt(h.current,iy),!h.current)return;if(typeof g=="number"){c.go(g);return}let v=Vi(g,JSON.parse(d),s,y.relative==="path");l==null&&i!=="/"&&(v.pathname=v.pathname==="/"?i:Yt([i,v.pathname])),(y.replace?c.replace:c.push)(v,y.state,y)},[i,c,d,s,l])}T.createContext(null);function g0(){let{matches:l}=T.useContext(Xt),i=l[l.length-1];return(i==null?void 0:i.params)??{}}function iu(l,{relative:i}={}){let{matches:c}=T.useContext(Xt),{pathname:o}=$t(),s=JSON.stringify(Qc(c));return T.useMemo(()=>Vi(l,JSON.parse(s),o,i==="path"),[l,s,o,i])}function v0(l,i){return cy(l,i)}function cy(l,i,c){var D;Ne(ul(),"useRoutes() may be used only in the context of a component.");let{navigator:o}=T.useContext(Rt),{matches:s}=T.useContext(Xt),d=s[s.length-1],h=d?d.params:{},p=d?d.pathname:"/",g=d?d.pathnameBase:"/",y=d&&d.route;{let U=y&&y.path||"";fy(p,!y||U.endsWith("*")||U.endsWith("*?"),`You rendered descendant (or called \`useRoutes()\`) at "${p}" (under ) but the parent route path has no trailing "*". This means if you navigate deeper, the parent won't match anymore and therefore the child routes will never render. + +Please change the parent to .`)}let v=$t(),S;if(i){let U=typeof i=="string"?al(i):i;Ne(g==="/"||((D=U.pathname)==null?void 0:D.startsWith(g)),`When overriding the location using \`\` or \`useRoutes(routes, location)\`, the location pathname must begin with the portion of the URL pathname that was matched by all parent routes. The current pathname base is "${g}" but pathname "${U.pathname}" was given in the \`location\` prop.`),S=U}else S=v;let M=S.pathname||"/",Y=M;if(g!=="/"){let U=g.replace(/^\//,"").split("/");Y="/"+M.replace(/^\//,"").split("/").slice(U.length).join("/")}let q=c&&c.state.matches.length?c.state.matches.map(U=>Object.assign(U,{route:c.manifest[U.route.id]||U.route})):$m(l,{pathname:Y});Gt(y||q!=null,`No routes matched location "${S.pathname}${S.search}${S.hash}" `),Gt(q==null||q[q.length-1].route.element!==void 0||q[q.length-1].route.Component!==void 0||q[q.length-1].route.lazy!==void 0,`Matched leaf route at location "${S.pathname}${S.search}${S.hash}" does not have an element or Component. This means it will render an with a null value by default resulting in an "empty" page.`);let N=A0(q&&q.map(U=>Object.assign({},U,{params:Object.assign({},h,U.params),pathname:Yt([g,o.encodeLocation?o.encodeLocation(U.pathname.replace(/%/g,"%25").replace(/\?/g,"%3F").replace(/#/g,"%23")).pathname:U.pathname]),pathnameBase:U.pathnameBase==="/"?g:Yt([g,o.encodeLocation?o.encodeLocation(U.pathnameBase.replace(/%/g,"%25").replace(/\?/g,"%3F").replace(/#/g,"%23")).pathname:U.pathnameBase])})),s,c);return i&&N?T.createElement(uu.Provider,{value:{location:{pathname:"/",search:"",hash:"",state:null,key:"default",mask:void 0,...S},navigationType:"POP"}},N):N}function b0(){let l=z0(),i=u0(l)?`${l.status} ${l.statusText}`:l instanceof Error?l.message:JSON.stringify(l),c=l instanceof Error?l.stack:null,o="rgba(200,200,200, 0.5)",s={padding:"0.5rem",backgroundColor:o},d={padding:"2px 4px",backgroundColor:o},h=null;return console.error("Error handled by React Router default ErrorBoundary:",l),h=T.createElement(T.Fragment,null,T.createElement("p",null,"💿 Hey developer 👋"),T.createElement("p",null,"You can provide a way better UX than this when your app throws errors by providing your own ",T.createElement("code",{style:d},"ErrorBoundary")," or"," ",T.createElement("code",{style:d},"errorElement")," prop on your route.")),T.createElement(T.Fragment,null,T.createElement("h2",null,"Unexpected Application Error!"),T.createElement("h3",{style:{fontStyle:"italic"}},i),c?T.createElement("pre",{style:s},c):null,h)}var S0=T.createElement(b0,null),sy=class extends T.Component{constructor(l){super(l),this.state={location:l.location,revalidation:l.revalidation,error:l.error}}static getDerivedStateFromError(l){return{error:l}}static getDerivedStateFromProps(l,i){return i.location!==l.location||i.revalidation!=="idle"&&l.revalidation==="idle"?{error:l.error,location:l.location,revalidation:l.revalidation}:{error:l.error!==void 0?l.error:i.error,location:i.location,revalidation:l.revalidation||i.revalidation}}componentDidCatch(l,i){this.props.onError?this.props.onError(l,i):console.error("React Router caught the following error during render",l)}render(){let l=this.state.error;if(this.context&&typeof l=="object"&&l&&"digest"in l&&typeof l.digest=="string"){const c=m0(l.digest);c&&(l=c)}let i=l!==void 0?T.createElement(Xt.Provider,{value:this.props.routeContext},T.createElement(Zc.Provider,{value:l,children:this.props.component})):this.props.children;return this.context?T.createElement(E0,{error:l},i):i}};sy.contextType=ay;var Tc=new WeakMap;function E0({children:l,error:i}){let{basename:c}=T.useContext(Rt);if(typeof i=="object"&&i&&"digest"in i&&typeof i.digest=="string"){let o=h0(i.digest);if(o){let s=Tc.get(i);if(s)throw s;let d=ty(o.location,c);if(ey&&!Tc.get(i))if(d.isExternal||o.reloadDocument)window.location.href=d.absoluteURL||d.to;else{const h=Promise.resolve().then(()=>window.__reactRouterDataRouter.navigate(d.to,{replace:o.replace}));throw Tc.set(i,h),h}return T.createElement("meta",{httpEquiv:"refresh",content:`0;url=${d.absoluteURL||d.to}`})}}return l}function _0({routeContext:l,match:i,children:c}){let o=T.useContext(ll);return o&&o.static&&o.staticContext&&(i.route.errorElement||i.route.ErrorBoundary)&&(o.staticContext._deepestRenderedBoundaryId=i.route.id),T.createElement(Xt.Provider,{value:l},c)}function A0(l,i=[],c){let o=c==null?void 0:c.state;if(l==null){if(!o)return null;if(o.errors)l=o.matches;else if(i.length===0&&!o.initialized&&o.matches.length>0)l=o.matches;else return null}let s=l,d=o==null?void 0:o.errors;if(d!=null){let v=s.findIndex(S=>S.route.id&&(d==null?void 0:d[S.route.id])!==void 0);Ne(v>=0,`Could not find a matching route for errors on route IDs: ${Object.keys(d).join(",")}`),s=s.slice(0,Math.min(s.length,v+1))}let h=!1,p=-1;if(c&&o){h=o.renderFallback;for(let v=0;v=0?s=s.slice(0,p+1):s=[s[0]];break}}}}let g=c==null?void 0:c.onError,y=o&&g?(v,S)=>{var M,Y;g(v,{location:o.location,params:((Y=(M=o.matches)==null?void 0:M[0])==null?void 0:Y.params)??{},pattern:i0(o.matches),errorInfo:S})}:void 0;return s.reduceRight((v,S,M)=>{let Y,q=!1,N=null,D=null;o&&(Y=d&&S.route.id?d[S.route.id]:void 0,N=S.route.errorElement||S0,h&&(p<0&&M===0?(fy("route-fallback",!1,"No `HydrateFallback` element provided to render during initial hydration"),q=!0,D=null):p===M&&(q=!0,D=S.route.hydrateFallbackElement||null)));let U=i.concat(s.slice(0,M+1)),Q=()=>{let X;return Y?X=N:q?X=D:S.route.Component?X=T.createElement(S.route.Component,null):S.route.element?X=S.route.element:X=v,T.createElement(_0,{match:S,routeContext:{outlet:v,matches:U,isDataRoute:o!=null},children:X})};return o&&(S.route.ErrorBoundary||S.route.errorElement||M===0)?T.createElement(sy,{location:o.location,revalidation:o.revalidation,component:N,error:Y,children:Q(),routeContext:{outlet:null,matches:U,isDataRoute:!0},onError:y}):Q()},null)}function Kc(l){return`${l} must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.`}function T0(l){let i=T.useContext(ll);return Ne(i,Kc(l)),i}function R0(l){let i=T.useContext(Qi);return Ne(i,Kc(l)),i}function O0(l){let i=T.useContext(Xt);return Ne(i,Kc(l)),i}function Jc(l){let i=O0(l),c=i.matches[i.matches.length-1];return Ne(c.route.id,`${l} can only be used on routes that contain a unique "id"`),c.route.id}function C0(){return Jc("useRouteId")}function z0(){var o;let l=T.useContext(Zc),i=R0("useRouteError"),c=Jc("useRouteError");return l!==void 0?l:(o=i.errors)==null?void 0:o[c]}function D0(){let{router:l}=T0("useNavigate"),i=Jc("useNavigate"),c=T.useRef(!1);return ry(()=>{c.current=!0}),T.useCallback(async(s,d={})=>{Gt(c.current,iy),c.current&&(typeof s=="number"?await l.navigate(s):await l.navigate(s,{fromRouteId:i,...d}))},[l,i])}var Rm={};function fy(l,i,c){!i&&!Rm[l]&&(Rm[l]=!0,Gt(!1,c))}T.memo(x0);function x0({routes:l,manifest:i,future:c,state:o,isStatic:s,onError:d}){return cy(l,void 0,{manifest:i,state:o,isStatic:s,onError:d})}function Xi({to:l,replace:i,state:c,relative:o}){Ne(ul()," may be used only in the context of a component.");let{static:s}=T.useContext(Rt);Gt(!s," must not be used on the initial render in a . This is a no-op, but you should modify your code so the is only ever rendered in response to some user interaction or state change.");let{matches:d}=T.useContext(Xt),{pathname:h}=$t(),p=oy(),g=Vi(l,Qc(d),h,o==="path"),y=JSON.stringify(g);return T.useEffect(()=>{p(JSON.parse(y),{replace:i,state:c,relative:o})},[p,y,o,i,c]),null}function Ft(l){Ne(!1,"A is only ever to be used as the child of element, never rendered directly. Please wrap your in a .")}function M0({basename:l="/",children:i=null,location:c,navigationType:o="POP",navigator:s,static:d=!1,useTransitions:h}){Ne(!ul(),"You cannot render a inside another . You should never have more than one in your app.");let p=l.replace(/^\/*/,"/"),g=T.useMemo(()=>({basename:p,navigator:s,static:d,useTransitions:h,future:{}}),[p,s,d,h]);typeof c=="string"&&(c=al(c));let{pathname:y="/",search:v="",hash:S="",state:M=null,key:Y="default",mask:q}=c,N=T.useMemo(()=>{let D=vn(y,p);return D==null?null:{location:{pathname:D,search:v,hash:S,state:M,key:Y,mask:q},navigationType:o}},[p,y,v,S,M,Y,o,q]);return Gt(N!=null,` is not able to match the URL "${y}${v}${S}" because it does not start with the basename, so the won't render anything.`),N==null?null:T.createElement(Rt.Provider,{value:g},T.createElement(uu.Provider,{children:i,value:N}))}function N0({children:l,location:i}){return v0(wc(l),i)}function wc(l,i=[]){let c=[];return T.Children.forEach(l,(o,s)=>{if(!T.isValidElement(o))return;let d=[...i,s];if(o.type===T.Fragment){c.push.apply(c,wc(o.props.children,d));return}Ne(o.type===Ft,`[${typeof o.type=="string"?o.type:o.type.name}] is not a component. All component children of must be a or `),Ne(!o.props.index||!o.props.children,"An index route cannot have child routes.");let h={id:o.props.id||d.join("-"),caseSensitive:o.props.caseSensitive,element:o.props.element,Component:o.props.Component,index:o.props.index,path:o.props.path,middleware:o.props.middleware,loader:o.props.loader,action:o.props.action,hydrateFallbackElement:o.props.hydrateFallbackElement,HydrateFallback:o.props.HydrateFallback,errorElement:o.props.errorElement,ErrorBoundary:o.props.ErrorBoundary,hasErrorBoundary:o.props.hasErrorBoundary===!0||o.props.ErrorBoundary!=null||o.props.errorElement!=null,shouldRevalidate:o.props.shouldRevalidate,handle:o.props.handle,lazy:o.props.lazy};o.props.children&&(h.children=wc(o.props.children,d)),c.push(h)}),c}var wi="get",Li="application/x-www-form-urlencoded";function Zi(l){return typeof HTMLElement<"u"&&l instanceof HTMLElement}function U0(l){return Zi(l)&&l.tagName.toLowerCase()==="button"}function B0(l){return Zi(l)&&l.tagName.toLowerCase()==="form"}function w0(l){return Zi(l)&&l.tagName.toLowerCase()==="input"}function L0(l){return!!(l.metaKey||l.altKey||l.ctrlKey||l.shiftKey)}function H0(l,i){return l.button===0&&(!i||i==="_self")&&!L0(l)}var xi=null;function j0(){if(xi===null)try{new FormData(document.createElement("form"),0),xi=!1}catch{xi=!0}return xi}var q0=new Set(["application/x-www-form-urlencoded","multipart/form-data","text/plain"]);function Rc(l){return l!=null&&!q0.has(l)?(Gt(!1,`"${l}" is not a valid \`encType\` for \`
\`/\`\` and will default to "${Li}"`),null):l}function Y0(l,i){let c,o,s,d,h;if(B0(l)){let p=l.getAttribute("action");o=p?vn(p,i):null,c=l.getAttribute("method")||wi,s=Rc(l.getAttribute("enctype"))||Li,d=new FormData(l)}else if(U0(l)||w0(l)&&(l.type==="submit"||l.type==="image")){let p=l.form;if(p==null)throw new Error('Cannot submit a +
+ +
+ ); +} diff --git a/frontend/src/components/ToastContainer.module.css b/frontend/src/components/ToastContainer.module.css new file mode 100644 index 0000000..91a2ec6 --- /dev/null +++ b/frontend/src/components/ToastContainer.module.css @@ -0,0 +1,50 @@ +.container { + position: fixed; + bottom: 24px; + right: 24px; + z-index: 10000; + display: flex; + flex-direction: column; + gap: 8px; + pointer-events: none; + max-width: min(360px, calc(100vw - 32px)); +} + +.toast { + pointer-events: auto; + padding: 12px 16px; + border-radius: 8px; + font-size: 14px; + line-height: 1.4; + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15); + animation: slideIn 0.2s ease-out; +} + +.success { + background: #ecfdf5; + color: #065f46; + border: 1px solid #a7f3d0; +} + +.error { + background: #fef2f2; + color: #991b1b; + border: 1px solid #fecaca; +} + +.warning { + background: #fffbeb; + color: #92400e; + border: 1px solid #fde68a; +} + +@keyframes slideIn { + from { + opacity: 0; + transform: translateY(8px); + } + to { + opacity: 1; + transform: translateY(0); + } +} diff --git a/frontend/src/components/ToastContainer.tsx b/frontend/src/components/ToastContainer.tsx new file mode 100644 index 0000000..654d1a2 --- /dev/null +++ b/frontend/src/components/ToastContainer.tsx @@ -0,0 +1,20 @@ +import type { ToastItem } from "../hooks/useToast"; +import styles from "./ToastContainer.module.css"; + +interface ToastContainerProps { + toasts: ToastItem[]; +} + +export function ToastContainer({ toasts }: ToastContainerProps) { + if (toasts.length === 0) return null; + + return ( +
+ {toasts.map((toast) => ( +
+ {toast.message} +
+ ))} +
+ ); +} diff --git a/frontend/src/components/annotations/AddAnnotationForm.tsx b/frontend/src/components/annotations/AddAnnotationForm.tsx deleted file mode 100644 index b95b8bb..0000000 --- a/frontend/src/components/annotations/AddAnnotationForm.tsx +++ /dev/null @@ -1,120 +0,0 @@ -import React, { useState } from "react"; -import { useAnnotations } from "@/context/AnnotationsContext"; - -interface AddAnnotationFormProps { - bookId: string; - page: number; - locationText?: string; - onClose?: () => void; -} - -export function AddAnnotationForm({ - bookId, - page, - locationText, - onClose, -}: AddAnnotationFormProps): React.ReactElement { - const { addBookmark, addNote } = useAnnotations(); - const [mode, setMode] = useState<"bookmark" | "note" | null>(null); - const [noteContent, setNoteContent] = useState(""); - const [submitting, setSubmitting] = useState(false); - const [error, setError] = useState(null); - - const handleSubmit = async (): Promise => { - setSubmitting(true); - setError(null); - try { - if (mode === "bookmark") { - await addBookmark({ book: bookId, page, location_text: locationText }); - } else if (mode === "note") { - if (!noteContent.trim()) { - setError("Note content cannot be empty."); - setSubmitting(false); - return; - } - await addNote({ - book: bookId, - page, - location_text: locationText, - content: noteContent.trim(), - }); - } - onClose?.(); - } catch (err) { - setError( - err instanceof Error ? err.message : "Failed to save annotation." - ); - } finally { - setSubmitting(false); - } - }; - - return ( -
-
- -

Add to Page {page}

- {locationText && ( -
- “{locationText}” -
- )} - - {!mode ? ( -
- - -
- ) : ( -
- {mode === "note" && ( -
- -